为什么`watch` 会让`ls /tmp` 列出$HOME 的内容?

Rus*_*lan 13 ls shell watch

我正在尝试查看/tmp/目录中的文件数。为此,我认为此命令会起作用:

watch sh -c 'ls /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)

但它似乎工作得好像ls没有参数一样。即,我在~,并且我在那里获得了文件数量而不是/tmp/. 我找到了一个解决方法,它似乎有效:

watch sh -c 'ls\ /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)

但是,为什么我要逃避之间的空间ls/tmp/?命令如何转换,watch以便将ls输出提供给wc,但/tmp/不作为参数传递给ls

thr*_*rig 18

可以通过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
Run Code Online (Sandbox Code Playgroud)

在反引号的情况下,ls /tmp作为单个参数传递给-cto sh,它按预期运行。如果没有这个反引号,命令会在watch运行时进行分词sh,然后运行提供的sh,因此只有ls作为参数传递给-c,这意味着子子sh将只运行一个裸ls命令,并列出当前工作的内容目录。

那么,为什么会出现并发症sh -c ...呢?为什么不简单地运行watch 'ls /tmp|wc -l'


Sté*_*las 8

有两个主要类别的watch命令(那些是定期运行命令的命令,watch不是标准命令,甚至watch有些系统会执行完全不同的操作,例如在 FreeBSD 上的另一条 tty 行上进行监听)。

一个已经将其参数与空格的连接传递给一个 shell(它实际上调用了sh -c <concatenation-of-arguments>),一个只运行指定参数指定的命令而不调用 shell。

您处于第一种情况,因此您只需要:

watch 'ls /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)

当你这样做时:

watch sh -c 'ls /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)

watch实际运行:

sh -c 'sh -c ls /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)

并且sh -c ls /tmp/正在运行lsinline 脚本 where $0is /tmp/(因此ls不带参数运行并列出当前目录)。

watch第一类中的一些实现(如 Linux 上的 procps-ng 中的实现)接受一个-x选项,使它们的行为类似于watch第二类。因此,在那里,您可以执行以下操作:

watch -x sh -c 'ls /tmp/|wc -l'
Run Code Online (Sandbox Code Playgroud)