ワイルドカード入力時に新しいファイル名を書き込む方法は?

ワイルドカード入力時に新しいファイル名を書き込む方法は?

私はこれまで参加した科学インターンシップで Inix 端末の使用経験があり、主に、、などのユーティリティをいくつか使用していましたgrepawksed私がしばらくの間考えていた、数値計算の効率を大幅に向上させる方法が 1 つあります。

大量のテキスト ファイルのコレクションに対していくつかの操作を実行するスクリプトがありますrun.awk。このスクリプトは、ファイルを取得しchloride.out、そこからデータを抽出して書き込みますchloride.cm

このスクリプトで、シェルの最初のワイルドカード フレーズに基づいてファイルを取得*.outおよび書き込む方法はありますか?*.cm

大量のデータを処理するために作成したスクリプトを 100 回以上繰り返し実行しなければならないのは、本当に面倒です。

理想的には、シェルを介してすべてのスクリプトでこれを行う方法があるかどうかを知りたいです。シェルまたは同等のもので自動化できない場合、少なくともawk私が説明したのと同様の方法でスクリプトを自動化できますか?

答え1

ワイルドカードを使用して、awk で複数のファイルを処理することはほぼ確実です。 1 つの提案としては、 を、run.awk単一のファイルを受け取り、単一の出力ファイルを生成する汎用の「関数」として残し、それを別のスクリプトから呼び出して、入力ファイルと出力ファイルを統合する方法があります。

これは Bash スクリプトなので、 と呼ぶことができますawk_runner.bash

#!/bin/bash

for ifname in *.out; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done

サンプル実行

いくつかのテストファイルを含むサンプルディレクトリを作成しました。

$ touch file{1..4}.out

その結果、4 つのファイルが作成されました。

$ ls -1
file1.out
file2.out
file3.out
file4.out

次にスクリプトを実行します:

$ ./awk_runner.bash
IN: file1.out, OUT: file1.cm
running run.awk with file1.out & file1.cm

IN: file2.out, OUT: file2.cm
running run.awk with file2.out & file2.cm

IN: file3.out, OUT: file3.cm
running run.awk with file3.out & file3.cm

IN: file4.out, OUT: file4.cm
running run.awk with file4.out & file4.cm

「実行中...」で始まる各行の後に、ここからスクリプトを実行できます。

リスト内のファイル

ワイルドカードを使用する代わりに、*.outファイル名のリストを含むファイルがあるとします。

$ cat filelist.txt 
file1.out
file2.out
file3.out
file4.out

whileループの代わりにループを使用する、スクリプトの修正バージョンを使用できますfor。次に、このスクリプトのバリアントを次のように呼びますawk_file_runner.bash

#!/bin/bash

while read ifname; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done < filelist.txt

このバージョンのスクリプトは、ファイルから入力を読み取りますfilelist.txt

done < filelist.txt

次に、ループの各ターンごとにwhilereadコマンドを使用して入力ファイルから 1 行を読み取ります。

while read ifname; do

次に、最初のスクリプトと同じ方法ですべてを実行し、ファイルの各行をループしながらawkスクリプトを実行します。run.awk

答え2

シェルラッパーを書いて、処理するファイルごとに新しいawkインスタンスを生成するのではなく、awkで直接これを行うことができます。awkスクリプトがすでにある場合は、FILENAME変数を使用して現在のファイルにアクセスできます。したがって、 を実行するとawk 'some commands' file1 file2、FILENAMEを使用してfile1またはfile2のどちらを操作しているかがわかります。awkで>on print/を使用することもできますprintf。したがって、次のようなawkスクリプトがある場合、

/pattern/{ print $1,$3 }

簡単にできる

/pattern/{ print $1,$3 > FILENAME".processed" }

または、FNR=1新しいファイルにいるかどうかを知るために使用し、ファイル名に対してより複雑な操作を行うための変数を作成します。拡張子.inを に置き換える.outなど、

sauer@humpy:/tmp$ grep . file*.in
file1.in:a
file1.in:b
file2.in:c
sauer@humpy:/tmp$ awk 'FNR=1{out=FILENAME;sub("\.in$",".out",out)} {print "processed"$0 > out}' file*.in
sauer@humpy:/tmp$ grep . file*.out
file1.out:processeda
file1.out:processedb
file2.out:processedc

grep .ここでは、ファイル名と複数のファイルの内容を表示するためにを使用していますが、これもまた楽しいトリックです。しかし、重要なのは、 が 1 に変わったときoutに変数の値を の修正バージョンに設定し(つまり、ファイルの 1 行目にある)、すべての出力を にリダイレクトすることです。拡張子が一致しないと置換が行われず、入力ファイルが上書きされるという点で、これはやや危険であることに注意してください。したがって、 またはそれに類するものを確実にするためのフェイルセーフ チェックを追加するとよいでしょう。これは読者の演習として残しておきます。 ;)FILENAMEFNRoutout != FILENAME

ファイル名のリストを含むファイルが必要な場合は、次のように実行するのが最も簡単です。

awkscript $(< /path/to/filename_list_file )

の内容を取得しfilename_list_fileてコマンド ラインに配置します。

関連情報