[Linux]ls + sed + bashで泥臭いファイル周りの作業が捗る話
Contents
はじめに
以前にも、[Linux]大量のファイル名のリネームをコマンド一発で実行するヒントという記事を書きましたが、このls + sed + bash
は使い勝手がよくて、今回いい感じの型が作れたのでメモに残します。
ケース1. 大量の行があるファイルから一部抜き出し、別のファイルに書き出す処理を、大量のファイルに対して行う
大量の行・ファイルを処理するシステムを実装中に、挙動確認用にテストデータを作りたいときなど。
前提
- フォルダaの中には数百のファイル
- それぞれのファイルの中身から一部抜き出し、フォルダbに同名ファイルで書き出す
コマンド
# a, bは同一ディレクトリ内に配置
$ ls -1 a | sed -r 's/.+/head a\/& > b\/&/' | bash
解説
ls -1 a
で、aの中身のファイル名を一覧表示します。-1
オプションでファイルごとに改行して表示します。
sed -r 's/.+/head a\/& > b\/&/'
で、lsで出した出力結果を正規表現を使って置換しています。
より分解すると:
.+
で対象範囲を指定します。ここでは1行まるごと(=ファイル名)対象にしています。head
で対象ファイルの冒頭10行を表示します>
でhead
の結果を指定したパスに書き出します&
は置換対象としている文字列全体を指します(ここではファイル名になります)
ということです。
例えば、
$ ls -1 a
hoge.tsv
fuga.tsv
piyo.tsv
として出力した内容を、
head a/hoge.tsv > b/hoge.tsv
head a/fuga.tsv > b/fuga.tsv
head a/piyo.tsv > b/piyo.tsv
という出力に変換する、という処理です。
最後に| bash
でこの出力をコマンドとして実行します。
ケース2. 大量のgzファイルの中身を一部抜き出し、別のファイルに書き出す処理
ケース1は対象がテキストファイルですが、サイズの大きなファイルだと、gz形式に圧縮されていることもしばしば。
そんなときは少しコマンドを改良します。
コマンド
$ ls -1 a/*.gz | sed -r 's/a\/.+/gzip -dc & | head > b\/\1/' | bash
解説
ls -1 a/*.gz
でa配下にある.gz
とつくファイルを一覧表示します。この場合、結果が
a/hoge.tsv,gz
a/fuga.tsv,gz
a/piyo.tsv,gz
のように、ディレクトリ名も含む点に注意が必要です。
sed -r 's/a\/(.+)\.gz/gzip -dc & | head > b\/\1/'
で、lsして出したファイルそれぞれを、解凍せずに中身を見てその一部を別ファイルに書き出しています。
余談ですがgzip -dc
は対象ファイルを解凍しないので、元となるファイルを変更せずに済みますし、むやみにファイルをコピーして容量を食う必要もないので重宝してます。
sedの部分をもう少し分解すると:
a\/(.+)\.gz
で、ディレクトリ名を含んだファイルパスから、ファイル名のみ、かつ.gz
という拡張子は除いた状態でキャプチャしますgzip -dc &
で、対象ファイルを解凍せずに中身を見ますhead >
の部分はケース1と同じですb\/\1
で、ファイル名を指定します。\1
はキャプチャした部分、つまりファイル名を意味します。
という感じになります。
例えば、
$ ls -1 a/*.gz
a/hoge.tsv,gz
a/fuga.tsv,gz
a/piyo.tsv,gz
として出力した結果を
gzip -dc a/hoge.tsv,gz | head > b/hoge.tsv
gzip -dc a/fuga.tsv,gz | head > b/fuga.tsv
gzip -dc a/piyo.tsv,gz | head > b/piyo.tsv
という出力に変換する、という処理です。
それを| bash
で実行する、というところはケース1と同様です。
まとめ
肝になるのはsed
による正規表現での扱いです。
やはり正規表現大事ですね。
ディスカッション
コメント一覧
まだ、コメントがありません