Por que 'assistir' faz o conteúdo da lista 'ls / tmp' de $ HOME?

13

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

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

Mas parece funcionar como se ls não tivesse argumentos. Ou seja, estou em ~ e recebo vários arquivos lá em vez de /tmp/ . Eu encontrei uma solução alternativa, que parece funcionar:

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

Mas por que eu preciso escapar do espaço entre ls e /tmp/ ? Como o comando é transformado por watch para que ls output seja alimentado para wc , mas /tmp/ não seja passado como argumento para ls ?

    
por Ruslan 29.01.2016 / 20:41

2 respostas

17

A diferença pode ser vista via 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 de backquote, ls /tmp é passado como um argumento único para o -c to sh , que é executado conforme o esperado. Sem esse backquote, o comando será dividido quando watch executar sh , que por sua vez executará o sh fornecido, de modo que somente ls seja passado como o argumento para -c , significando que o sub-sub sh executará apenas o comando ls , e listará o conteúdo do diretório de trabalho atual.

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

    
por 29.01.2016 / 21:00
8

Existem duas categorias principais de comandos watch (daqueles que devem executar comandos periodicamente, watch não é um comando padrão, existem até sistemas em que watch faz algo completamente diferente como bisbilhotar em outro tty linha 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 um que apenas executa o comando especificado com os argumentos especificados sem invocar um shell.

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

watch 'ls /tmp/|wc -l'

Quando você faz:

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

seu watch é executado:

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

E sh -c ls /tmp/ está executando o script ls in-line, em que $0 é /tmp/ (portanto, ls é executado sem argumentos e lista o diretório atual).

Algumas das implementações watch na primeira categoria (como a do procps-ng no Linux) aceitam uma opção -x para fazê-las se comportar como watch da segunda categoria. Então, com isso, você pode fazer:

watch -x sh -c 'ls /tmp/|wc -l'
    
por 29.01.2016 / 22:10

Tags