AWKが呼び出され、^ C != 0で中断されると、bashは終了します。

AWKが呼び出され、^ C != 0で中断されると、bashは終了します。

次の(G)AWKスクリプトフラグメントについて質問があります。

do {
    ...
} while (system("sleep 10"))

私の意図は、ユーザーがスリープ中に^ Cを押したときにループを切断することですが、うまくいきません。

問題は、少なくともAWKによって実行されたときに^ Cで中断されると、Bashが0で終了することですsystem()

$ awk 'BEGIN { print "\n" system("sleep 2") }'
(let the sleep complete)
0

 

$ awk 'BEGIN { print "\n" system("sleep 2") }'
^C
0

なぜですか?

これはBashまたは(G)AWKのバグですか?

たとえば、複雑なBash固有の構文を含まない単純な解決策はありますかtrap

私が考えることができる最善は次のとおりです。

do {
    ...
} while (42 == system("sleep 10 && exit 42"))

私にとって、それはまだ間違った砂利のように感じます。

答え1

system()何を返すべきですか?あいまいに指定

実装で一般的に見えるのは、awk通常の終了時に終了コード(モジュールとして256に渡された数字exit(3))を返しますが、シェルプロセスが信号によって終了したときに動作が大きく異なることです。

さらに、C関数は親項目でSIGINT(およびSIGQUIT)を無視するように設計されていますが、その要件がssystem(3)にも適用されるかどうかは(少なくとも私にとっては)明確ではありません。いくつかの実装(例:)はそのSIGINTで終了します(関数を実行しているという理由だけでCTRL-Cを無視するのが好きではないため、見たい動作でもあります)。一部(たとえば、または従来の実装)にあります。awksystem()awkmawkawksystem()gawk

さらに、一部のシェルはこれらの信号の一部を傍受して最終的に呼び出すことができ、exit()これは動作に影響を与える可能性があります(Bourneシェルの説明の説明を参照)。これはexec、以下の例でシェルを削除するために使用するものです。ループ理由。

SIGINTに返された値(1をsystem()考慮するとより多くのバリアント)について、次のことがわかります。close()

$ nawk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ bwk-awk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ mawk 'BEGIN {print system("exec kill -s INT $$")}'
130
$ gawk 'BEGIN {print system("exec kill -s INT $$")}'
0

0.00781252 / 256(コアがダンプされている場合は0.542969((128 + 11)/256)SEGV、そうでない場合は0.0429688(11 / 256))、11Solaris 10または11、またはLinuxポートのHeirloom Toolboxにありnawkますnawkbwk-awkawkBrian Kernighanが直接管理K中央awk)いくつかのBSDで見つけることができる基本事項awk(Debian GNU / Linuxでテスト済み)。/usr/xpg4/bin/awkSolaris 11での動作はgawk

したがって、s返された値system(3)(ビット0〜6は信号番号、ビット7はコアビット、ビット8〜15は終了コードの整数)に基づいて、上記の結果は次のようにawk返されますsystem()

  • s / 256(伝統的なawk実装)、
  • int(s/256)gawk)、
  • または、mawkBourneやCシェルなどのシェルが実行するのと同じ変換((s&127)+128終了した場合、s>>8そうでない場合)を除いて、コアがダンプされている場合の(s&127)+256代わりに(s&127)+128(の値(s&255)+128)を取得します。

ここでは、次のようにすることができます。

awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10")}'

ただし、一部の実装がawk終了する可能性があります。あなたのものまたはあなたの場合は、次のことができます。awkmawkshbashyash

awk 'BEGIN{print system("set -m; sleep 10; exit")}'

したがって、sleepそれは独自のプロセスグループで実行されます(そしてSIGINTのみが得られます)。

別のオプションは、呼び出す前にSIGINTを無視することですawk。ただし、ほとんどのシェル(POSIX要件)は、起動時にシグナルが無視されるとシグナルハンドラーを変更することはできません。だから、次のようになります。

(
  trap '' INT
  awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10; exit")}'
)

動作しません。zshただし、そのような(自己発生)制限はないため、利用可能であることがわかっているzsh場合は、次のことができます。

(
  trap '' INT
  awk 'BEGIN{print system("exec zsh -c \"TRAPINT() exit 1; sleep 10\"")}'
)

awkどちらかが機能し、mawkジョブgawk制御を妨げない可能性があります。ただし、この時点では、必要に応じて信号処理を調整できるperl// python...rubyの代わりに使用を検討する価値があります。awk

ノート

1close()パイプの上:

awk 'BEGIN {cmd = "kill -s INT $$"; cmd | getline; print close(cmd)}'

まず、これは私が試したすべての実装で^C中断されます(SIGINT / SIGQUITがawkforを無視したり、好きな要件はありません(これを実装する自然な方法))。popen(3)pclose(3)getlinesystem(3)

sただし、終了状態(上記のような/返された値がある場所)については、次のことがわかります。pclose(3)waitpid(2)system()

  • Solaris nawk: 動作しません。close()Solarisではそれを呼び出すことはできませんnawk
  • /usr/xpg4/bin/awkソラリスから。exit(1)プロセスが完了しても常にゼロを返します。明らかに一貫性エラーです。
  • gawkbwk-awk:提供sexit 1256提供、SIGINTでキル2提供、SIGSEGV 11でキル、コア提供139)。
  • mawk:と同じで、これを考慮した唯一の実装のようですsystem()mawk

関連情報