为什么 `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以便ls将输出提供给wc,但/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作为单个参数传递给-cto sh,它按预期运行。如果没有这个反引号,命令在watch运行时会进行分词sh,然后运行提供的sh,因此 onlyls作为参数传递给-c,这意味着子子sh将仅运行一个裸ls命令,并列出当前工作的内容目录。

那么,为什么会变得复杂呢sh -c ...?为什么不直接跑呢watch 'ls /tmp|wc -l'

答案2

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

一种已经将其参数与空格的串联传递给 shell(它实际上确实是 call sh -c <concatenation-of-arguments>),另一种只是运行使用指定参数指定的命令而不调用 shell。

你处于第一种情况,所以你只需要:

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选项,使它们的行为类似于watch第二类。因此,有了那里,您可以执行以下操作:

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

相关内容