ORing mit true in einem Befehl über ssh

15

Wenn ich versuche, pkill -fremote über ssh zu arbeiten und den möglichen Fehlercode zu verwerfen (um mit dem Rest meines Skripts fortzufahren, auch wenn kein Prozess gefunden wurde), || trueverhalte ich mich nicht wie erwartet.

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255

Ich nehme an, dass es so ist ssh ist , das 255 zurückgibt, nicht den Befehl zwischen Anführungszeichen, aber warum?

Gauthier
quelle

Antworten:

29

Ihre Annahme, dass es sich sshselbst handelt, der den Exit-Status 255 zurückgibt, ist korrekt. In der sshManpage heißt es:

ssh wird mit dem Exit-Status des Remote-Befehls oder mit 255 beendet, wenn ein Fehler aufgetreten ist.

Wenn Sie einfach ausführen würden ssh [email protected] "pkill -f asdf", würden Sie höchstwahrscheinlich einen Beendigungsstatus von erhalten 1, der dem pkillStatus für " Keine Prozesse gefunden " entspricht.

Die Herausforderung besteht darin, zu verstehen, warum bei der Ausführung von SSH ein Fehler auftritt

ssh pi@10.20.0.10 "pkill -f asdf || true"

SSH-Fernbefehle

Der SSH-Server startet eine Shell zum Ausführen von Remote-Befehlen. Hier ist ein Beispiel dafür in Aktion:

$ ssh server "ps -elf | tail -5"
4 S root     35323  1024 12  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony [priv]
5 S anthony  35329 35323  0  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony@notty
0 S anthony  35330 35329  0  80   0 - 28283 do_wai 12:01 ?        00:00:00 bash -c ps -elf | tail -5
0 R anthony  35341 35330  0  80   0 - 40340 -      12:01 ?        00:00:00 ps -elf
0 S anthony  35342 35330  0  80   0 - 26985 pipe_w 12:01 ?        00:00:00 tail -5

Beachten Sie, dass die Standard-Shell ist bashund dass der Remote-Befehl kein einfacher Befehl, sondern eine Pipeline ist , „eine Folge von einem oder mehreren Befehlen, die vom Steueroperator getrennt werden| ".

Die Bash - Shell klug genug , um zu erkennen , dass , wenn der Befehl von der an sie übergeben werden -cOption ist ein einfacher Befehl , es durch nicht wirklich Forking einen neuen Prozess zu optimieren, das heißt, es direkt execs den einfachen Befehl statt durch den zusätzlichen Schritt des Gehens von forking , bevor es execs. Hier ist ein Beispiel dafür, was passiert, wenn Sie einen einfachen Remote-Befehl ausführen ( ps -elfin diesem Fall):

$ ssh server "ps -elf" | tail -5
1 S root     34740     2  0  80   0 -     0 worker 11:49 ?        00:00:00 [kworker/0:1]
1 S root     34762     2  0  80   0 -     0 worker 11:50 ?        00:00:00 [kworker/0:3]
4 S root     34824  1024 31  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony [priv]
5 S anthony  34829 34824  0  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony@notty
0 R anthony  34830 34829  0  80   0 - 40340 -      11:51 ?        00:00:00 ps -elf

Ich habe dieses Verhalten schon einmal erlebt, konnte aber keine bessere Referenz als diese AskUbuntu-Antwort finden .

Pkill-Verhalten

Da pkill -f asdf || truees sich nicht um einen einfachen Befehl handelt (es handelt sich um eine Befehlsliste ), kann die oben genannte Optimierung nicht durchgeführt werden. Wenn Sie diesen Befehl ausführen ssh [email protected] "pkill -f asdf || true", wird er vom sshdProzess gegabelt und ausgeführt bash -c "pkill -f asdf || true".

Wie die Antwort von ctx zeigt, pkillwird es seinen eigenen Prozess nicht beenden. Es wird jedoch jeder andere Prozess beendet, dessen Befehlszeile dem -fMuster entspricht. Der bash -cBefehl stimmt mit diesem Muster überein, sodass er diesen Prozess abbricht - seinen eigenen übergeordneten Prozess (wie es geschieht).

Der SSH-Server stellt dann fest, dass der Shell-Prozess, den er zum Ausführen der Remotebefehle gestartet hat, unerwartet beendet wurde, sodass ein Fehler an den SSH-Client gemeldet wird.

Anthony G - Gerechtigkeit für Monica
quelle
1
Während die Antwort die Quelle eines Problems korrekt identifiziert, wie dies pkillbeim Beenden des übergeordneten Shell-Prozesses der Fall ist, weil die Argumentliste mit dem regulären Ausdruck übereinstimmt, werde ich einen terminologischen Einwand erheben: Es x || yhandelt sich nicht um einen zusammengesetzten Befehl , sondern um eine Befehlsliste .
Stéphane Chazelas
@ StéphaneChazelas Danke für das Feedback. Ich habe mich dafür entschieden, dass Bash bedingte Konstrukte als zusammengesetzte Befehle aufgenommen hat, aber ich stimme zu, dass es logischer ist, sie x||yals Befehlsliste zu betrachten . Ich habe meine Antwort jetzt so bearbeitet, dass sie Links zu den verschiedenen POSIX-Definitionen enthält.
Anthony G - Gerechtigkeit für Monica
1
Beachten Sie, dass es im Allgemeinen nicht so sehr optimiert werden kann, da es sich um eine Befehlsliste handelt , für die (möglicherweise) ein anderer Befehl ausgeführt werden muss. In zsh/ ksh93/ FreeBSD sh, false || pkill -f asdfwäre pkillin dem Shell - Prozess ausgeführt. bashDie Optimierung wird nur ausgeführt, wenn nur ein einfacher Befehl vorhanden ist. true; pkill -f asdfwäre auch ein problem.
Stéphane Chazelas
9

Ihr Remote-Befehl bringt sich selbst um:

$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true

pgrep und pkill ignorieren ihren eigenen Prozess, aber mit dem Flag -f finden sie die übergeordnete Shell:

$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true
ctx
quelle
Das macht Sinn! bash -c 'pgrep -af asdf'(ohne die || true) findet sich nicht . Warum nicht? Es hat -f.
Gauthier
2
@Gauthier Tatsächlich denke ich, dass Bash in diesem Fall klug genug ist, um zu erkennen, dass der Befehl einfach ist (kein zusammengesetzter Befehl), sodass er optimiert wird, indem er keinen neuen Prozess forkt. Ich erinnere mich, dass ich zuvor auf ein ähnliches Verhalten gestoßen bin (ich muss meine Antwort aktualisieren).
Anthony G - Gerechtigkeit für Monica
3

Sie bitten pkill, alles zu töten, was mit "asdf" übereinstimmt. Sie sollten festlegen, dass es mit [a] sdf übereinstimmt. Auf diese Weise sucht es weiterhin nach "asdf", sieht sich jedoch nicht selbst (wenn Sie asdf an [a] sdf ausrichten, beachten Sie, dass das s an] und ausgerichtet ist nicht s.)

ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'

Dies ist ein allgemeiner Trick, der auch mit grep / egrep / awk / etc verwendet wird:

ps -ef | grep "something"  # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself

# why it works:
# the commandline contains:     ps -ef | grep [s]omething
# and grep tries to find:                      something

Dieser Trick ist alt und ich habe ihn vor Jahrzehnten in der Unix-FAQ gesehen (was immer noch eine gute Lektüre ist!)

Um es zu "automatisieren", ist es nicht einfach, aber normalerweise können Sie versuchen, jedes Mal, wenn Sie nach einer variablen Zeichenfolge regexp = "something" suchen, Folgendes zu tun:

grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')" 
#  if regexp="something",  it does: grep "[s]omething"
#  if regexp="otherthing", it does: grep "[o]therthing"
#  if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...
Olivier Dulac
quelle
Hinweis: Mir ist bewusst, dass mein Grep-Beispiel 'fail' so bleiben könnte, wie es ist, da es sich selbst nicht entspricht (das a, b oder c stimmt nicht mit dem ']' der Kommandozeile überein). . Aber es ist nicht trivial, einen Test für den regulären Ausdruck zu finden. Im Allgemeinen funktioniert der Trick. Diejenige, die automatisiert, wird die meiste Zeit funktionieren. Wenn nicht, sind einige clevere Hacks (oder manuelle Eingriffe) erforderlich.
Olivier Dulac
Außerdem (abc)?(def)?muss es sein ([a]bc)?([d]ef)?... Sie können Regex nicht mit Regex analysieren ?! > :-)
wizzwizz4
@ wizzwizz4 Ich weiß. aber dein Beispiel passt schon nicht zu sich. Dies ist eine komplexe Sache, ich habe nur eine einfache Lösung für einfachere Fälle bereitgestellt
Olivier Dulac
@ wizzwizz4 Das sage ich schon in meinem ersten Kommentar ...
Olivier Dulac