はじめに
ふざけたタイトルをつけてしまったので、内容をざっくりと説明すると、「あるディレクトリに含まれているファイルのうち(ファイル名でソートされているものとして)上からn個のファイルを別ディレクトリにまとめてコピーする」ということを(シェルスクリプトを書いたりしてゴニョゴニョするのも面倒なので)1行のコマンドで行えないものかと思い、調査した際の備忘録になります。
上記の目的を果たすためにどうしたかを述べただけであり、体系的にまとまっているわけではない点はご容赦ください。
設定
状況設定として/Datasets/A
というディレクトリにdata00.h5
~ data99.h5
という100個のデータがあり、このうち上から20個を/Datasets/B
というディレクトリにコピーしたいとします(ここではh5データを仮定していますが、h5データでなくても問題ありません)。
上から10個であれば、ワイルドカードを使って
$ cp /Datasets/A/data0*.h5 /Datasets/B/.
などとすることもできますが、このやり方では10個以外の場合ではうまくいきません。
そこで、ls
コマンドやhead
コマンドなどをうまく組み合わせて、ディレクトリにある任意の数のデータをまとめてコピーしたいというのが今回の趣旨になります。
パイプライン処理について
まず簡単にパイプライン処理についておさらいしたいと思います。
パイプライン処理というのは、あるコマンドの出力を別のコマンドの入力として使うことで、複数のコマンドを繋げパイプラインにして行う処理のことです。
例として、先程の/Datasets/A
にあるデータのうち上から5個だけを表示するには次のようにします。
~/Datasets/A$ ls -1 | head -5
ls -1
はディレクトリ内のファイルの一覧をリストとして縦に出力するコマンドです。
これを実行すると次のようになります。
~/Datasets/A$ ls -1
data00.h5
data01.h5
(中略)
data99.h5
またhead -5
は入力のうち上から5行だけを表示するコマンドになっていて、|
で二つのコマンドを繋ぐことで、ls -1
の出力をhead -5
の入力とすることができ、その出力は次のようになります。
~/Datasets/A$ ls -1 | head -5
data00.h5
data01.h5
data02.h5
data03.h5
data04.h5
本題
ディレクトリ内のファイルのうち上位20件をコピーするには、パイプライン処理を使ってls
コマンド、head
コマンド、そしてcp
コマンドを組み合わせると上手くいきそうな気がします。
しかし、cp
では二つ引数を取ることが問題になります。つまり
~/Datasets/A$ ls -1 | head -20 | cp /Datasets/B/.
のようにしてしまうと、/Datasets/B/
というのがコピー元のパスなのかコピー先のパスなのかが明らかでなく、エラーが出てしまいます。
そこでxargs
というコマンドを用います。
xargs
コマンドというのは「パイプライン処理を明示的に行うためのコマンド」というように筆者は理解しています。用途としては様々あるのですが、ここでは-i
というオプションにより、直前のコマンドの出力を次のコマンドのどの位置で引数にするかを指定できることを利用します。
つまり、これによりls -1 | head -20
の出力をcp
コマンドの一つ目の引数とすることを明示的に指示することが可能になります。具体的には次のようにします。
~/Datasets/A$ ls -1 | head -20 | xargs -i cp {} /Datasets/B/.
-i
オプションを使うことで{}
の位置に直前のコマンドの出力が入力されます。
以上のようにすることで、ディレクトリ/Datasets/A
にあるファイルのうち上から20個のファイルdata00.h5
~ data19.h5
を無事、ディレクトリDatasets/B
にコピーすることができました。
おわりに
やったこととしては全然大したことはやっていないのですが、パイプライン処理は上手く組み合わせると何でもできる気がしてきて楽しいですね。今回は「xargs
コマンドってすごいね!パイプライン処理楽しい!」っていうお話でした。