Почему `watch` делает` ls / tmp` список содержимого $ HOME?

13

Я пытаюсь посмотреть количество файлов в моем /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
watch "sh -c 'ls /tmp | wc -l'"выполнение этой команды должно получить желаемый эффект. Это не ошибка часов, попробуйте, sh -c ls /tmpи вы получите свой домашний каталог (но я понятия не имею, почему ...)
Джейкоб Минсхолл
8
Не ответ, но вы используете watchнеправильно. Команда, которую вы передаете, watchв свою очередь подаетсяwatch , чтобы sh -c, таким образом вы фактически делаете sh -cдважды.
iruvar
Если вам интересно, вы также можете взглянуть на источник .
Михас
1
@JacobMinshall, почему это просто: /tmp аргумент sh, в этом случае не аргумент ls.
Чарльз Даффи

Ответы:

17

Разницу можно увидеть через 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, так что только lsпередается в качестве аргумента -c, а это означает , что суб-суб shбудет работать только голую lsкоманду, и перечисляет содержимое текущей работы каталог.

Итак, почему осложнение sh -c ...? Почему бы просто не бежать watch 'ls /tmp|wc -l'?

thrig
источник
О, действительно, не думал пытаться straceэто сделать.
Руслан
1
На самом деле, `это обратная цитата (или обратная галочка). Этот вопрос о \, который является обратной косой чертой.
G-Man говорит: «Восстановите Монику»
@Ruslan: я разместил этот комментарий к этому ответу, потому что это комментарий к этому ответу . thrig говорит : «В кавычка случае, ls /tmpэто ...» и «Без этой обратной кавычки , команда ...», и использует bqи nobqкак имена файлов, когда все время ссылаясь на обратную косую черту в вашей ls\ /tmpкоманде.
G-Man говорит: «Восстановите Монику»
8

Есть две основные категории watch команд (из которых, которые должны периодически запускать команды, watchэто не стандартная команда, есть даже системы, в которых watchесть что-то совершенно иное, например, отслеживание другой строки tty во FreeBSD).

Тот, который уже передает конкатенацию своих аргументов с пробелами в оболочку (в действительности это вызывает 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реализаций в первой категории (например, из procps-ng в Linux) принимают -xопцию, чтобы заставить их вести себя так же, как watchво второй категории. Так что с там вы можете сделать:

watch -x sh -c 'ls /tmp/|wc -l'
Стефан Шазелас
источник