なぜ `watch` は `ls /tmp` に $HOME の内容をリストさせるのですか?

なぜ `watch` は `ls /tmp` に $HOME の内容をリストさせるのですか?

ディレクトリ内のファイル数を表示しようとしています/tmp/。これを行うには、次のコマンドが機能すると思います。

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

lsしかし、議論なしに動作するようです。つまり、私はにあり、~代わりにそこにあるファイルの数を得ています/tmp/。うまくいくように見える解決策を見つけました。

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

lsしかし、なぜ私との間のスペースを抜けなければなりませんか/tmp/?出力はに提供されますが、引数watchに渡されないようにコマンドをどのように変換できますか?lswc/tmp/ls

答え1

違いは、次の方法で確認できます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単一引数として渡されるバックティックの場合、期待どおりに機能します。このバックティックがない場合、コマンドは実行時にトークン化され、提供されたものを実行するため、引数としてのみ渡されます。つまり、subziは基本コマンドのみを実行し、現在の作業ディレクトリの内容を一覧表示します。-cshwatchshshls-cshls

それではなぜ複雑になるのでしょうかsh -c ...?なぜそれだけ走らないのですかwatch 'ls /tmp|wc -l'

答え2

コマンドには2つの主なカテゴリがありますwatch(コマンドを定期的に実行するコマンドはwatch標準コマンドではなく、一部のシステムはwatchFreeBSDで他のtty行を聞くなど、まったく異なるタスクを実行する場合があります)。

1つは既に空白になっている引数をシェルに渡し(実際には呼び出しを実行しますsh -c <concatenation-of-arguments>)、もう1つはシェルを呼び出さずに指定された引数で指定されたコマンドを実行します。

最初の状況にあるので、次のものが必要です。

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実装など)では、2番目のカテゴリと-x同じように動作するオプションがあります。watchしたがって、ここで次のことができます。

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

関連情報