Bashでコマンド出力結果を使ってコマンドを実行する方法

# はじめに
LinuxのBashでコマンド操作を行なっている際に、コマンドが出力した結果をそのままコマンドとして利用したり、実行したいと思う場面に遭遇する事があるかと思いますが、その場合の対応方法について紹介します。

例えばファイル名のプレフィックスを誤って付与して複数ファイルを生成してしまいファイル名を変更したい場合、リネームするためのコマンドを生成したものの、この後どうやって生成したコマンドを実行しよう...となってしまった場合などが上記に該当します。

```bash:プレフィックスのhoge1をhoge2に変更するコマンドを生成
$ ls
hoge1_20220530.dmp  hoge1_20220530.png  hoge1_20220530.txt
$ ls | sed -e 's/^\(.*\)/mv \1 :\1/' -e 's/:hoge1/hoge2/'
mv hoge1_20220530.dmp hoge2_20220530.dmp
mv hoge1_20220530.png hoge2_20220530.png
mv hoge1_20220530.txt hoge2_20220530.txt
$ 
```

## ・コンソールにコマンドを貼り付ける

コマンドの出力結果を一行コピーしてコンソールに貼り付けて実行、終わったら次の一行をコピーしてコンソールに貼り付けて実行...と繰り返し行う事もできますが、大量に行数があった場合に作業が面倒になります。

意外と知られていませんがコンソールにコマンドを貼り付けて実行する場合に複数行のコピー結果をコンソールに貼り付ける事ができます。
また、貼り付けられてたコマンドは上から順番に実行されるので、わざわざ一行一行実行されるのを待つ必要はありません。

上記の例でいうと、コマンド出力結果の下記3行を改行と一緒にコピーしてコンソールに貼り付ると、そのまま3つのコマンドが上から順次実行されて行きます。

```bash
mv hoge1_20220530.dmp hoge2_20220530.dmp
mv hoge1_20220530.png hoge2_20220530.png
mv hoge1_20220530.txt hoge2_20220530.txt
```

1つのコマンドに時間がかかる処理でも順番に実行してくれるので、テキストエディタなどに記載した複数のコマンドを手っ取り早く実行する際にも役に立ちます。

## ・`bash`コマンドを使用する

コマンドを実行する際のShellとして認識されているBashですが、実は`bash`というコマンドが用意されており、それを利用してコマンド出力結果を使ってコマンドを実行する事ができます。

具体的にはコマンドの出力結果をパイプ`|`で繋いで`bash`コマンドに渡す事で、渡された出力結果のテキストをコマンドとして実行してくれます。

```bash:プレフィックスのhoge1をhoge2にmvする(bashコマンド使用)
$ ls | sed -e 's/^\(.*\)/mv \1 :\1/' -e 's/:hoge1/hoge2/' | bash
$  
```

末尾に `| bash` をつけるだけなので、簡単でわかりやすくオススメの実行方法です。

## ・`xargs`コマンドを使用する

コマンドの引数を動的に作成して実行するための代表的なコマンドとして`xargs`コマンドがありますが、このコマンドを利用してコマンド出力結果を使ってコマンドを実行する事ができます。

具体的にはコマンドの出力結果をパイプ`|`で繋いで`xargs`コマンドを通じて一行ずつ`sh`コマンドの引数として渡す事で、渡された出力結果のテキストをコマンドとして実行してくれます。

```bash:プレフィックスのhoge1をhoge2にmvする(xargsコマンド使用)
$ ls | sed -e 's/^\(.*\)/mv \1 :\1/' -e 's/:hoge1/hoge2/' | xargs -IX sh -c "X"
$ 
```

また、せっかく`xargs`コマンドを使用するのであればリネームするための`mv`コマンドを`xargs`コマンドに持ってくる方がスッキリと記述できます。

```bash:プレフィックスのhoge1をhoge2にmvする(xargsコマンド使用)
$ ls | sed -e 's/^\(.*\)/\1 :\1/' -e 's/:hoge1/hoge2/' | xargs -L1 mv
$ 
```


`bash`コマンドの方が簡単でわかりやすいので、わざわざ`xargs`コマンドを使う必要はありませんが、引数も動的に変える場合や、時間のかかるコマンドを`-P`オプションを使用して複数並列に実行する場合などに重宝します。

```bash:3コマンド同時並行で実行
$ ls | sed -e 's/^\(.*\)/mv \1 :\1/' -e 's/:hoge1/hoge2/' | xargs -P3 -IX sh -c "X"
$ 
```

今回は単純に出力結果を使用してコマンドを実行するだけなので`xargs`コマンド自体の詳細な説明は割愛しますが、非常に便利で強力なコマンドなので、興味のある方は是非調べてみてください。


## ・`sed`コマンドを使用する

今回の例でもコマンド出力結果のテキストを加工するために使用した`sed`コマンドですが、引数のスクリプトコマンドに`e`を指定する事でテキストをコマンドとして実行する事ができます。

```bash:プレフィックスのhoge1をhoge2にmvする(sedコマンド使用)
$ ls | sed -e 's/^\(.*\)/mv \1 :\1/' -e 's/:hoge1/hoge2/e'
$ 
```

上記では置換処理を行なった後にコマンドの実行を行なっていますが、わかりやすくするために`ls`という文字列を`sed`コマンドに渡し、置換せずに実行したものを下記に記載します。

```bash:echoでlsという文字列を出力し、パイプでsedに渡してlsを実行
$ echo ls
ls
$ echo ls | sed 'e'
hoge1_20220530.dmp
hoge1_20220530.png
hoge1_20220530.txt
$ 
```

また、簡単な置換処理を行なった後にコマンドを実行したものを下記に記載します。

```bash:echoでlsという文字列を出力し、パイプでsedに渡して-aオプションを指定してlsを実行(ls -aを実行)
$ echo ls | sed 's/$/ -a/e'
.
..
hoge1_20220530.dmp
hoge1_20220530.png
hoge1_20220530.txt
$ 
```

この様に、実行したいコマンドを作成しながら実行もできるので非常に使いやすく、`sed`コマンドそのものも非常に強力なため、慣れてくると一番活躍する実行方法になるのではないかと思います。

## ・コマンド置換`$()`を使用する

Bashにはコマンドの出力結果を置き換えるコマンド置換(`$()`)という機能があり、この機能を利用してコマンドの出力結果をそのままコマンドとして実行する事ができます。


```bash:lsをコマンド置換で実行
$ echo ls | $(cat -)
hoge1.txt  hoge2.txt
$ 
```

通常コマンド置換は変数にコマンド出力結果を格納するのに使用されますが、この様にコマンドの出力結果をそのままコマンドとして使用することもできます。
ただ、一行のコマンドしか実行できず非常にわかり難いため、この方法を使用するより素直に`bash`コマンドを使用する方が良いと思います。

## ・`find`コマンドを使用する

コマンドの出力結果をそのままコマンドとして実行する訳ではないのですが、`find`コマンドでも見つけたファイルに対してコマンドを実行する事ができます。

例えば先ほど例として紹介したファイルのリネームについても、`find`コマンドであればスッキリ記述する事ができます。

```bash:ファイルのプレフィックスであるhoge1をhoge2に変更
$ find ./ -exec rename hoge1 hoge2 "{}" \;
$ 
```

この様に、検索したファイルに対してアクションを起こしたい場合は`find`コマンドでできる事が以外と多く、また`xargs`コマンドを組み合わせて使うことも多いので参考までに記載しておきます。