Warum listet `watch` mit` ls / tmp` den Inhalt von $ HOME auf?

13

Ich versuche, die Anzahl der Dateien in meinem /tmp/Verzeichnis zu überwachen . Dafür dachte ich, dass dieser Befehl funktionieren würde:

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

Aber es scheint zu funktionieren, als ob lses keine Argumente gäbe . Ich bin nämlich dabei ~und erhalte stattdessen die Anzahl der Dateien /tmp/. Ich habe eine Problemumgehung gefunden, die zu funktionieren scheint:

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

Aber warum muss ich dem Raum zwischen lsund entfliehen /tmp/? Wie wird der Befehl von transformiert, watchsodass die lsAusgabe an geleitet wc, aber /tmp/nicht als Argument an übergeben wird ls?

Ruslan
quelle
1
watch "sh -c 'ls /tmp | wc -l'"Das Ausführen dieses Befehls sollte den gewünschten Effekt erzielen. Dies ist kein Uhrenfehler, versuchen sh -c ls /tmpSie es und Sie erhalten Ihr Home-Verzeichnis (aber ich habe keine Ahnung warum ...)
Jacob Minshall
8
Keine Antwort, aber Sie verwenden sie watchfalsch. Der Befehl, an den Sie übergeben werden, watchwird wiederum watchan weitergeleitet sh -c, sodass Sie tatsächlich sh -czweimal vorgehen .
Iruvar
Wenn Sie neugierig sind, können Sie auch einen Blick auf die Quelle werfen .
Michas
1
@JacobMinshall, das Warum ist einfach: Das /tmpist ein Argument dafür sh, in diesem Fall kein Argument dafür ls.
Charles Duffy

Antworten:

17

Der Unterschied ist ersichtlich über 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

Im backquote-Fall ls /tmpwird als einziges Argument das -cto übergeben sh, das wie erwartet abläuft. Ohne diese Backquote ist der Befehl anstelle Wort Split , wenn watchläuft , shwas wiederum die mitgelieferte läuft sh, so dass nur noch lsals Argument übergeben wird -c, was bedeutet , dass das Sub-Sub shnur einen nackten läuft lsBefehl, und listet den Inhalt des aktuellen Arbeits Verzeichnis.

Warum also die Komplikation von sh -c ...? Warum nicht einfach rennen watch 'ls /tmp|wc -l'?

Thrig
quelle
Oh ja, ich habe nicht daran gedacht, es zu versuchen strace.
Ruslan
1
Eigentlich ist `backquote (oder back-tick). Diese Frage handelt von \, einem Backslash.
G-Man sagt, dass Monica am
@ Ruslan: Ich habe diesen Kommentar zu dieser Antwort gepostet , weil es sich um einen Kommentar zu dieser Antwort handelt . thrig sagt : „Im Backquote Fall ls /tmpist ...“ und „Ohne diese Backquote , der Befehl ist ...“, und Anwendungen bqund nobqals Dateinamen, wenn die ganze Zeit auf den Bezug Backslash in Ihrem ls\ /tmpBefehl.
G-Man sagt, dass Monica am
8

Es gibt zwei Hauptkategorien von watchBefehlen (von denen, die Befehle in regelmäßigen Abständen ausführen sollen, watchkein Standardbefehl sind, es gibt sogar Systeme, in denen watchetwas völlig anderes ausgeführt wird, wie das Aufspüren einer anderen tty-Zeile unter FreeBSD).

Eine, die bereits die Verkettung ihrer Argumente mit Leerzeichen an eine Shell übergibt (dies führt tatsächlich einen Aufruf durch sh -c <concatenation-of-arguments>), und eine, die nur den mit den angegebenen Argumenten angegebenen Befehl ausführt, ohne eine Shell aufzurufen.

Du bist in der ersten Situation, also brauchst du nur:

watch 'ls /tmp/|wc -l'

Wenn Sie das tun:

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

Ihr watchläuft tatsächlich:

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

Und sh -c ls /tmp/führt das lsInline-Skript aus, in dem sich $0befindet /tmp/(wird also lsohne Argumente ausgeführt und listet das aktuelle Verzeichnis auf).

Einige der watchImplementierungen in der ersten Kategorie (wie die von procps-ng unter Linux) akzeptieren eine -xOption, mit der sie sich wie die watchder zweiten Kategorie verhalten . Mit there können Sie also Folgendes tun:

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