ファイル名に=が含まれている場合、awkが停止して待機するのはなぜですか?


回答:


19

クリスは言う、フォームの引数はvariablename=anything((新しい)とは対照的に、引数が処理されている時に実行されている変数への代入として扱われる-v var=value前に実行されているものBEGIN文)の代わりに、入力ファイル名。

次のような場合に役立ちます。

awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2

ファイルごとに異なるFS/ を指定できる場所RS。また、一般的に以下で使用されます。

awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2

どちらがより安全なバージョンです:

awk 'NR==FNR{a[$0]; next}; {...}' file1 file2

file1が空の場合は機能しません)

しかし、名前に=文字が含まれているファイルがある場合、それは邪魔になります。

さて、それは最初の残りが=有効なawk変数名である場合にのみ問題になります。

で有効な変数名を構成するものawkは、よりも厳密ですsh

POSIXでは、次のようにする必要があります。

[_a-zA-Z][_a-zA-Z0-9]*

ポータブル文字セットの文字のみ。ただし、/usr/xpg4/bin/awkSolaris 11の少なくともその点では準拠しておらず、a-zA-Zだけでなく、変数名のロケールでアルファベット文字を使用できます。

そのため、x+y=fooorや=barorのような引数./foo=barは、入力ファイル名として扱われ、最初の引数の残りが=有効な変数名ではないため、割り当てとしては扱われません。実装とロケールにStéphane=Chazelas.txt応じて、引数のような場合とそうでない場合がawkあります。

そのため、awkでは次の使用をお勧めします。

awk '...' ./*.txt

の代わりに

awk '...' *.txt

たとえば、txtファイルの名前に=文字が含まれないことを保証できない場合に問題を回避するためです。

また、次のような引数を-vfoo=bar.txtオプションとして扱う場合があることに注意してください:

awk -f file.awk -vfoo=bar.txt

(も適用されawk '{code}' -vfoo=bar.txtawkbusyboxのバージョンから前1.28.0に、参照対応するバグレポートを)。

繰り返しになりますが、これを./*.txt回避することで回避できます(./プレフィックスを使用すると、代わりに標準入力を意味するものとして理解されるファイルが呼び出さ-れます)。awk

それも理由です

#! /usr/bin/awk -f

シバンは実際には機能しません。一方でvar=valueものをすることで回避することができ、固定ARGV値を(追加./で接頭辞)BEGINの文:

#! /usr/bin/awk -f
BEGIN {
  for (i = 1; i < ARGC; i++)
    if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
      ARGV[i] = "./" ARGV[i]
}
# rest of awk script

これらawkawkスクリプトではなくオプションによって表示されるため、オプションでは役立ちません。

その./接頭辞を使用することで生じる可能性のある表面的な問題の1つは、で終わることになりますが、必要でない場合FILENAMEはいつでも使用できsubstr(FILENAME, 3)ます。

GNUの実装によりawk、これらの問題はすべて-Eオプションで修正されます。

の後-E、gawkはawkスクリプトのパス(-まだstdinを意味する)のみを期待し、入力ファイルのパスのみのリストを期待します(そして、-特別な扱いもされません)。

以下のために特別に設計されています:

#! /usr/bin/gawk -E

引数のリストが常に入力ファイルであるシェバンス(ステートメントARGV内でそのリストを自由に編集できることに注意してBEGINください)。

次のように使用することもできます。

gawk -e '...awk code here...' -E /dev/null *.txt

-E空のスクリプト(/dev/null)を使用するのは、*.txtそれらに=文字が含まれている場合でも、後でそれらが常に入力ファイルとして扱われるようにするためです。


FILENAMEで終わる明示的なパスがどのように問題になるかわかりません。awkスクリプトは一般的であり、それはFILENAMEで終わるパスのすべての種類を処理する必要があり、その場合には(を含むがこれらに限定されないのいずれか../foo/path/to/fooおよび異なるエンコーディングであるパス) -その場合にはsubstr(FILENAME,3)十分な、またはそれのないであろうユーザーが基本的にファイル名を知っているワンショットスクリプトです。この場合、おそらく=どちらも含む
ファイルに煩わさ

2
@mosvyそれはそれほど./問題だとは思わないが、出力にファイル名を含める./必要がある場合など、特定の条件下では望ましくない場合があり、その場合は冗長で不要である必要があるため、どういうわけかそれを取り除く必要があります。以下に少なくとも1つの例を示します。ユーザーがファイル名を知っている場合-この場合、ファイル名も知っていますが、=それでも適切な処理の妨げになります。リード-が邪魔をすることができます。
セルギーKolodyazhnyy

@mosvy、はい、アイデアはあなたが./そのawk(誤)機能を回避するためにプレフィックスを使用したいが、それからあなたは./あなたがストリップしたいかもしれない出力でそれで終わるということです。ファイルの最初の行に特定の文字列が含まれているかどうかを確認する方法を参照してください?例として。
ステファンシャゼル

ローカル(このディレクトリに相対的)./だけでなく、/awkに引数をファイルとして解釈させるグローバル(絶対パス)でもあります。
アイザック

21

awkのほとんどのバージョンでは、実行するプログラムの後の引数は次のいずれかです。

  1. ファイル
  2. フォームの割り当て x=y

ファイル名はケース#2として解釈されているため、awkはまだstdinで何かを読むのを待っています(ファイル名が渡されたことを認識しないため)。

移植性があるため、この動作はPOSIX文書化されています。

次の2種類の引数のいずれかを混在させることができます。

  • file:読み込む入力を含むファイルのパス名。これは、プログラム内の一連のパターンと照合されます。ファイルオペランドが指定されていない場合、またはファイルオペランドが「-」の場合、標準入力が使用されます。
  • 割り当て:ポータブル文字セットのアンダースコアまたはアルファベット文字で始まるオペランド(IEEE Std 1003.1-2001、セクション6.1、ポータブル文字セットの基本定義ボリュームの表を参照)、その後に一連のアンダースコア、数字、ポータブル文字セットのアルファベット文字とそれに続く「=」文字は、パス名ではなく変数の割り当てを指定するものとします。

そのため、移植性があるため、いくつかのオプションがあります(#1が最も邪魔にならない可能性が高い)。

  1. を使用しますawk ... ./my=file。これは、.「ポータブル文字セットのアンダースコアまたはアルファベット文字」ではないためです。
  2. を使用してファイルを標準入力に配置しawk ... < my=fileます。ただし、これは複数のファイルではうまく機能しません。
  3. ファイルへのハードリンクを一時的に作成し、それを使用します。のようなことをしてln my=file my_fileから、my_file通常どおり使用できます。コピーは実行されず、両方のファイルは同じデータとiノードのメタデータによってバックアップされます。それを使用した後、iノードへの参照の数はまだ0より大きいため、作成されたリンクを削除しても安全です。

6
動作しません./my=file か? 有効な変数名ではない% awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory). ため、これは移植性があるはず./myです。したがって、そのように解析されるべきではありません。
スティーブンハリス

2
POSIXのテキストにあるように、問題は、最初の文字の=前にポータブル文字セットのアンダースコアまたはアルファベット文字がある場合のみです(IEEE Std 1003.1-2001、セクション6.1、ポータブル文字セットのベース定義ボリュームの表を参照)。ポータブル文字セットからのアンダースコア、数字、およびアルファベットのシーケンスが続きます。以下のようなファイルパスはそう++foo=bar.txtか、=fooまたは./foo=barされているすべてのOKはそのよう.+ではありません[_a-zA-Z]
ステファンシャゼル

1
@SergiyKolodyazhnyy awkはシェルの外部にあるため、どちらを使用してもかまいません。./my=file逐語的に渡されます。
クリスダウン

1
@SergiyKolodyazhnyy、も同じですawk '{print $1,$2}' /etc/passwd。要点は、awkとは対照的にシェルにファイルを開かせても、シーク可能にするかどうかについては違いはありません。実際、でawk '{exit}' < /etc/passwdawk最初のレコードの末尾に戻って、exitそこにあるstdin内の位置から離れることを確認します。POSIXにはそれが必要です。/usr/xpg4/bin/awkそれは、Solaris上で行いますが、どちらgawkmawkGNU / Linux上でそれを行うように見えます。
ステファンシャゼル

3
@mosvy、参照INPUT FILESのでセクションをpubs.opengroup.org/onlinepubs/9699919799/utilities/...それだけであなたがでそれにファイルまたは書き込みデータを切り捨てるしたいときのような通常のファイルで意味をなすことを利用パターンの数に有用ですawkそのようにして特定された位置。
ステファンシャゼル

3

gawkのドキュメントを引用するには(注意を強調してください):

コマンドラインの追加の引数は、通常、指定された順序で処理される入力ファイルとして扱われます。ただし、var = valueという形式の引数は、値valueを変数varに割り当てます。ファイルをまったく指定しません。

コマンドが停止して待機するのはなぜですか?フォームawk 'processing_script_here' my=file.txt には上記の定義で指定されたファイルがありません - my=file.txt変数割り当てとして解釈れ、定義れたファイルがない場合awkはstdinを読み取ります(straceそのようなコマンドのawkがread(0,'...)syscall で待機していることからも明らかです

これは、に記載されてPOSIXのawkの仕様オペランド部と、参照、代入)その一部を

変数の割り当ては、/ etc / passwdのすべての行にのawk '{print foo}' foo=bar /etc/passwdfooが出力されることから明らかです。./foo=barただし、フルパスを指定しても機能します。

実行straceawk '1' foo=barて確認するとcat foo=bar、これはawk固有の問題であり、execveは渡された引数としてファイル名を表示するため、この場合、シェルはenv変数の割り当てとは関係ありません。

さらに、awk '...script...' foo=bar環境変数の割り当てを有効にしてコマンドの前に置く必要があるため、シェルによる環境変数の作成は発生しません。POSIX Shell Grammar Rules、ポイント番号7を参照してください。さらに、これはawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.