Por que o `watch` faz o conteúdo da lista` ls / tmp` do $ HOME?

13

Estou tentando assistir o número de arquivos no meu /tmp/diretório. Por isso, pensei que este comando funcionaria:

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

Mas parece funcionar como se lsnão tivesse argumentos. Ou seja, estou dentro ~e recebo o número de arquivos lá em vez de /tmp/. Encontrei uma solução alternativa, que parece funcionar:

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

Mas por que preciso escapar do espaço entre lse /tmp/? Como o comando é transformado watchpara que a lssaída seja alimentada wc, mas /tmp/não seja passada como argumento para ls?

Ruslan
fonte
1
watch "sh -c 'ls /tmp | wc -l'"esse comando deve ter o efeito desejado. Isto não é relógios culpa, tentativa sh -c ls /tmpe você vai ter a sua pasta pessoal (mas não tenho idéia do porquê ...)
Jacob Minshall
8
Não é uma resposta, mas você estiver usando watchincorretamente .O comando que você passa para watchpor sua vez é alimentada por watchque sh -c, então você está na verdade fazendo sh -cduas vezes.
iruvar 29/01
Se você está curioso, também pode dar uma olhada na fonte .
Michas
1
@ JacobMinshall, o porquê é direto: o /tmpargumento é sh, nesse caso, não o argumento ls.
Charles Duffy

Respostas:

17

A diferença pode ser vista através de 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

No caso da citação retroativa, ls /tmpé passado como um único argumento para o -cpara sh, que é executado conforme o esperado. Sem essa citação retroativa, o comando é dividido em palavras quando watchexecutado, shque por sua vez executa o fornecido sh, para que apenas lsseja passado como argumento para -c, o que significa que o sub-sub shapenas executará um lscomando bare e lista o conteúdo do trabalho atual. diretório.

Então, por que a complicação de sh -c ...? Por que não simplesmente correr watch 'ls /tmp|wc -l'?

agitar
fonte
Ah, de fato, não pensei em tentar strace.
Ruslan
1
Na verdade, `está entre aspas (ou back-tick). Esta pergunta é sobre \, que é uma barra invertida.
G-Man diz 'Reinstate Monica'
@Ruslan: Postei este comentário nesta resposta porque é um comentário sobre esta resposta . thrig diz "No backquote caso, ls /tmpé ..." e "Sem esta crase , o comando é ...", e os usos bqe nobqcomo nomes de arquivos, quando ao mesmo tempo se referindo à barra invertida em seu ls\ /tmpcomando.
G-Man Diz 'Reinstate Monica'
8

Existem duas categorias principais de watchcomandos (dos que devem executar comandos periodicamente, watchnão é um comando padrão, existem até sistemas em watchque algo completamente diferente é como bisbilhotar em outra linha tty no FreeBSD).

Um que já passa a concatenação de seus argumentos com espaços para um shell (na verdade, chama sh -c <concatenation-of-arguments>) e outro que apenas executa o comando especificado com os argumentos especificados sem chamar um shell.

Você está na primeira situação, então só precisa de:

watch 'ls /tmp/|wc -l'

Quando você faz:

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

seu watchrealmente é executado:

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

E sh -c ls /tmp/está executando o lsscript embutido onde $0está /tmp/(assim lsé executado sem argumentos e lista o diretório atual).

Algumas das watchimplementações na primeira categoria (como a do procps-ng no Linux) aceitam uma -xopção para fazê-las se comportar como a watchda segunda categoria. Então, com lá, você pode fazer:

watch -x sh -c 'ls /tmp/|wc -l'
Stéphane Chazelas
fonte