web-dev-qa-db-ja.com

`watch`が` ls / tmp`に$ HOMEの内容を一覧表示させるのはなぜですか?

/tmp/ディレクトリ内のファイルの数を監視しようとしています。このため、私はこのコマンドが機能すると思いました:

watch sh -c 'ls /tmp/|wc -l'

しかし、lsに引数がないかのように機能しているように見えます。つまり、私は~にいて、/tmp/の代わりにそこにファイルの数を取得します。私は回避策を見つけました、それはうまくいくようです:

watch sh -c 'ls\ /tmp/|wc -l'

しかし、なぜls/tmp/の間のスペースをエスケープする必要があるのですか?コマンドはwatchによってどのように変換されてls出力がwcに送られますが、/tmp/は引数としてlsに渡されませんか?

13
Ruslan

違いはstraceで確認できます。

$ strace -ff -o bq watch sh -c 'ls\ /tmp/|wc -l'
^C
$ strace -ff -o nobq watch sh -c 'ls /tmp/|wc -l'
^C
$ grep exec bq* | grep sh
bq.29218:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls\\ /tmp/|wc -l"], [/* 54 vars */]) = 0
bq.29219:execve("/bin/sh", ["sh", "-c", "sh -c ls\\ /tmp/|wc -l"], [/* 56 vars */]) = 0
bq.29220:execve("/bin/sh", ["sh", "-c", "ls /tmp/"], [/* 56 vars */]) = 0
$ grep exec nobq* | grep sh
nobq.29227:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls /tmp/|wc -l"], [/* 54 vars */]) = 0
nobq.29228:execve("/bin/sh", ["sh", "-c", "sh -c ls /tmp/|wc -l"], [/* 56 vars */]) = 0
nobq.29229:execve("/bin/sh", ["sh", "-c", "ls", "/tmp/"], [/* 56 vars */]) = 0

バッククォートの場合、ls /tmpは単一の引数として-cからshに渡され、期待どおりに実行されます。このバッククォートがないと、コマンドは代わりにwatchshを実行するときにワード分割され、次に指定されたshが実行されるため、lsのみが-cの引数。これは、sub-sub shが裸のlsコマンドのみを実行し、現在の作業ディレクトリの内容を一覧表示することを意味します。

では、なぜsh -c ...の複雑さは?単にwatch 'ls /tmp|wc -l'を実行してみませんか?

17
thrig

watchコマンドには2つの主要なカテゴリがあります(コマンドを定期的に実行するもののうち、watchは標準コマンドではなく、watchが完全に何かを実行するシステムもありますFreeBSDの別のtty行をスヌーピングするようなものです)。

引数とスペースの連結をすでにシェルに渡しているもの(実際にはsh -c <concatenation-of-arguments>を呼び出します)と、シェルを呼び出さずに指定された引数で指定されたコマンドを実行するだけのものです。

あなたは最初の状況にあるので、あなたはただ必要です:

watch 'ls /tmp/|wc -l'

あなたがするとき:

watch sh -c 'ls /tmp/|wc -l'

watchは実際に実行されます:

sh -c 'sh -c ls /tmp/|wc -l'

そして、sh -c ls /tmp/lsインラインスクリプトを実行しています。ここで$0/tmp/です(したがって、lsは引数なしで実行され、現在のディレクトリを一覧表示します)。

最初のカテゴリのwatch実装の一部(Linuxのprocps-ngの実装など)は、-xオプションを受け入れて、2番目のカテゴリのwatchのように動作させます。そこで、次のことができます。

watch -x sh -c 'ls /tmp/|wc -l'
8