왜 `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인수 로 전달되지 않도록 명령을 어떻게 변환할 수 있습니까 ?lswc/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단일 인수로 전달된 백틱의 경우 예상대로 작동합니다. 이 백틱이 없으면 명령은 실행 시 토큰화되고 제공된 을 실행하므로 인수로만 전달됩니다 . 즉, subzi는 기본 명령만 실행 하고 현재 작업 디렉터리의 내용을 나열합니다.-cshwatchshshls-cshls

그렇다면 왜 복잡해지는 걸까요 sh -c ...? 왜 그냥 달리지 않는 걸까요 watch 'ls /tmp|wc -l'?

답변2

명령에는 두 가지 주요 범주가 있습니다 watch(명령을 주기적으로 실행하는 명령은 watch표준 명령이 아니며 일부 시스템은 watchFreeBSD에서 다른 tty 라인을 듣는 것과 같이 완전히 다른 작업을 수행하기도 합니다).

하나는 이미 공백으로 연결된 인수를 셸에 전달하고(실제로는 호출을 수행함 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(Linux의 procps-ng 구현과 같은) 에서는 두 번째 범주 -x처럼 동작하도록 하는 옵션을 허용합니다. watch따라서 여기에서 다음을 수행할 수 있습니다.

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

관련 정보