1. 概要

awkコマンドとsedコマンドはどちらも、強力なLinuxコマンドラインテキスト処理ユーティリティです。 sed コマンドには、入力ファイルを「インプレース」で編集するための便利な-iオプションがあることがわかっています。 つまり、変更を入力ファイルに保存することができます。

このチュートリアルでは、例を通してawkコマンドを使用して「インプレース」編集を行う方法を探ります。

2. 入力ファイルの例

awk コマンドを見る前に、scores.txtという名前の入力ファイルを作成しましょう。

$ cat scores.txt
Kai 77
Eric 97.5
Amanda 97
Jerry 60
Tom 80

上記の出力が示すように、テキストファイルには学生の名前と試験のスコアが含まれています。

awk コマンドを使用して、ファイルの最後に平均スコアを追加します。

次に、ファイルをインプレースで編集する方法を見てみましょう。

3. インプレースGNUの拡張機能awkの使用

GNU awk は、広く使用されているawkの実装です。 リリース4.1.0以降、GNU awkには、GNU sedの-i(インプレース)オプションをエミュレートするためのインプレース拡張機能が付属しています。

3.1. -i inplaceオプションの使用

gawkコマンドのinplace拡張機能を使用するための構文は非常に簡単です。

gawk -i inplace '... awk code ...' input_files

上記の-iオプションは、awkソースライブラリを含めるためのものです。 この場合、inplace拡張機能を含めます。 したがって、次の引数があります。-iinplace。

それでは、平均スコアを計算してscores.txtに追加しましょう。

$ gawk -i inplace '{sum+=$2} 1; ENDFILE {printf "----\nAVG: %.2f\n",sum/NR}' scores.txt
$ cat scores.txt
Kai 77
Eric 97.5
Amanda 97
Jerry 60
Tom 80
----
AVG: 82.30

すごい! ファイルは期待どおりに更新されました。

3.2. ENDvs。 ENDFILE

前のセクションのawkone-linerは非常に簡単です。 すべてのスコアを合計し、最後に平均値を計算します。

ただし、ワンライナーを注意深く読むと、通常のENDブロックの代わりにENDFILEブロックを使用していることがわかります。

END ブロックを使用した場合も同じでしょうか? 少しテストして調べてみましょう。

次のテストでは、ヘッダー「ファイルの始まり」とフッター「ファイルの終わり」score.txtに追加します。 ] ファイル。

まず、BEGINブロックとENDブロックにそれぞれヘッダーとフッターを出力しましょう。

$ gawk -i inplace 'BEGIN { print "The beginning of the file" } 
                   { print }
                   END { print "The end of the file" }' scores.txt
The beginning of the file
The end of the file

上記の出力が示すように、ヘッダーとフッターが印刷されます。 ただし、ファイル内のレコードは表示されませんでした。 ただし、心配しないでください。 inplace 拡張機能が含まれているため、とにかく入力ファイルを更新する必要があります。 入力ファイルscores.txtを確認してみましょう。

$ cat scores.txt
Kai 77
Eric 97.5
Amanda 97
Jerry 60
Tom 80

おっとっと! ヘッダーとフッターはファイルに追加されません! なぜそれが起こったのですか?

これが発生した理由を説明するには、inplace拡張機能がどのように機能するかを理解する必要があります。

インプレース拡張子は、ファイルの処理中にのみ意味があります。ただし、BEGINおよびENDブロックは、ファイルが処理される前とファイルの後に実行されます。完全に処理されました。

したがって、インプレース拡張機能は、BEGINおよびENDブロックの変更では機能しません。 この問題を解決するには、GNU awkの2つの特定のパターンを使用する必要があります。BEGINFILEENDFILEです。

$ gawk -i inplace 'BEGINFILE { print "The beginning of the file" } 
                   { print }
                   ENDFILE { print "The end of the file" }' scores.txt

上記のコマンドを実行した後は出力がありませんが、ヘッダーとフッターがscore.txtファイルに追加されています。

$ cat scores.txt
The beginning of the file
Kai 77
Eric 97.5
Amanda 97
Jerry 60
Tom 80
The end of the file

3.3. 複数のファイルをインプレースで編集する

インプレース拡張機能は、複数の入力ファイルでも機能します。 例を通してそれに対処しましょう。

まず、3つの小さなファイルを作成しましょう。

$ head *.txt
==> java.txt <==
Java!

==> kotlin.txt <==
Kotlin!

==> linux.txt <==
Linux!

次に、短い awk ワンライナーを作成して、各ファイルのテキストを変更します。

$ gawk -i inplace '$0 = "I Love " $0' java.txt kotlin.txt linux.txt

最後に、3つのファイルを再確認しましょう。

$ head *.txt                               
==> java.txt <==
I Love Java!

==> kotlin.txt <==
I Love Kotlin!

==> linux.txt <==
I Love Linux!

涼しい! すべてのファイルがインプレースで変更されます。

awk コードでは、各ファイルを個別に処理したり、リダイレクトを手動で制御したりする必要はありません。 代わりに、 awk コマンドがファイルから各行を読み取るときに、テキストを変更するだけです。 インプレース拡張機能は、現在処理されているファイルを処理し、変更をファイルに自動的に書き戻します。

4. 一時ファイルの使用

GNU awk がない場合、または gawk バージョンが4.1.0未満の場合、便利なinplace拡張機能を使用できません。 気にしないでください—一時ファイルを使用して変更を入力ファイルに保存することができます。

awk '... code ...' input_file > tmp_file && mv tmp_file input_file

使用したので && 演算子、 awk コマンドが正常に実行されると、後で実行されます mv コマンドを実行します。

一時ファイルを介してscores.txtに平均スコアを追加しましょう。

$ awk '{sum+=$2} 1; END {printf "----\nAVG: %.2f\n",sum/NR}' scores.txt > score.tmp && mv score.tmp scores.txt

$ cat scores.txt
Kai 77
Eric 97.5
Amanda 97
Jerry 60
Tom 80
----
AVG: 82.30

例が示すように、一時ファイルを使用してインプレース編集を行うことも非常に便利です。 ただし、このアプローチは、複数の入力ファイルにインプレース編集を適用する場合は機能しないことに注意してください。

5. いくつかの一般的な落とし穴

これまで、awkコマンドを使用してインプレース編集を行う2つの異なる方法を見てきました。

ここで、最初に、推奨されていないインプレース編集方法をいくつか紹介します。 次に、それらが推奨されるソリューションではない理由を理解して、現実の世界でそれらを使用しないようにします。

5.1. エコーコマンドとコマンド置換

コマンド置換は、コマンドの出力を取得するためのシェルスクリプトの便利な手法です。 echo コマンドで使用して、変更を入力ファイルにリダイレクトする場合があります。

echo "$(awk  '... code ...' input_file)" > input_file

この方法は簡単に見えます。 awk コマンドが正常に実行されれば、正しく機能します。 ただし、awkコマンドでエラーが発生した場合、は入力データを破壊する可能性があります。

この方法を使用して、平均スコアをscore.txtに追加しましょう。

$ echo "$(awk '{sum+ =$2} 1; END {printf "----\nAVG: %.2f\n",sum/NR}' scores.txt)" > scores.txt                                                                             
awk: cmd. line:1: {sum+ =$2} 1; END {printf "----\nAVG: %.2f\n",sum/NR}
awk: cmd. line:1:       ^ syntax error

上記の例では、 sum+=の間に誤ってスペースを入力しました。 当然のことながら、 awk コマンドは不平を言い、エラーメッセージを出力しました。

エラーを修正して、コマンドを再実行することをお勧めします。 ただし、その前に、入力ファイルを確認しましょう。

$ cat scores.txt
$

おっとっと! 入力ファイルのデータがなくなりました!

これが起こったのは失敗したawkコマンドは何も出力しませんでした stdout。 さらに、 エコーコマンドは空の文字列を出力し、それを入力ファイルにリダイレクトしました。

したがって、入力ファイルを処理するためにこの方法を使用するべきではありません。

5.2. リダイレクトの落とし穴

IOリダイレクトを使用すると、多くの問題を適切に解決できます。 場合によっては、リダイレクトを操作して変更を入力ファイルに保存しようとするコードが表示されることがあります。

command < input_file > input_file

この方法はスマートでコンパクトに見えます。 ただし、機能しません。 さらに、このようなコマンドを実行すると、input_fileがとにかく切り捨てられるため、危険です。

これは、コマンドが実行される前に、シェルによって解釈される特別な表記法を使用して、コマンドの入力と出力がリダイレクトされる可能性があるためです。 つまり、 シェルは、コマンドに制御を渡す前にリダイレクトを実行します。 したがって、「 >> 」リダイレクトは input_fileを空にします 。 したがって、コマンドが実行され、 input_file から読み取りたいときまでに、ファイルはすでに空になっています。

catコマンドを使用して少しテストしてみましょう。

$ cat < scores.txt > scores.txt 
$ wc -c scores.txt
0 scores.txt

6. 結論

この記事では、awkコマンドを使用してインプレース編集を行う方法について説明しました。 例を通していくつかのアプローチに取り組みました。

さらに、実際に注意すべきいくつかの一般的な落とし穴について話しました。