![ORing mit true in einem Befehl über SSH](https://rvso.com/image/122433/ORing%20mit%20true%20in%20einem%20Befehl%20%C3%BCber%20SSH.png)
Wenn ich versuche, die Ausführung pkill -f
remote über SSH durchzuführen und den möglichen Fehlercode zu verwerfen (um mit dem Rest meines Skripts fortzufahren, auch wenn kein Prozess gefunden wird), || true
verhält es sich nicht wie erwartet.
$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh [email protected] "pkill asdf || true"
$ echo $?
0
$ ssh [email protected] "pkill -f asdf || true"
255
Ich nehme an, es istsshdas gibt 255 zurück, nicht den Befehl in Anführungszeichen, aber warum?
Antwort1
Ihre Annahme, dass es ssh
selbst den Exit-Status 255 zurückgibt, ist richtig. Auf der ssh
Manpage steht:
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 , würden Sie höchstwahrscheinlich den Beendigungsstatus erhalten , der dem Status für „ entspricht.ssh [email protected] "pkill -f asdf"
1
pkill
Keine Prozesse gefunden“.
Die Herausforderung besteht darin, zu verstehenWarumein Fehler mit SSH auftritt, wenn Sie
ssh [email protected] "pkill -f asdf || true"
SSH-Remote-Befehle
Der SSH-Server startet eine Shell, um Remote-Befehle auszuführen. 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 bash
und dass der Remote-Befehl kein einfacher Befehl ist, sondern einPipeline, „eine Folge von einem oder mehreren Befehlen, die durch den Steueroperator getrennt sind |
“.
Die Bash-Shell ist intelligent genug, um zu erkennen, dass es sich bei dem ihr durch die Option übergebenen Befehl -c
um eineneinfacher Befehl, kann es optimieren, indem es nicht wirklich einen neuen Prozess aufspaltet, d. h. es exec
führt den einfachen Befehl direkt aus, anstatt den zusätzlichen Schritt des fork
Ausführens auszuführen, bevor es exec
ausgeführt wird. Hier ist ein Beispiel dafür, was passiert, wenn Sie einen einfachen Remotebefehl ausführen ( ps -elf
in 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 bin schon einmal auf dieses Verhalten gestoßen, konnte aber keine bessere Referenz finden alsdiese AskUbuntu-Antwort.
pkill-Verhalten
Da pkill -f asdf || true
ist kein einfacher Befehl (es ist einBefehlsliste), kann die obige Optimierung nicht erfolgen, sodass beim Ausführen der Prozess sich aufspaltet und ausgeführt wird .ssh [email protected] "pkill -f asdf || true"
sshd
bash -c "pkill -f asdf || true"
Wie die Antwort von ctx zeigt, pkill
wird es seinen eigenen Prozess nicht beenden. EsWilleBeenden Sie alle anderen Prozesse, deren Befehlszeile dem -f
Muster entspricht. Der bash -c
Befehl entspricht diesem Muster und beendet daher diesen Prozess – seinen eigenen übergeordneten Prozess (wie es der Zufall will).
Der SSH-Server erkennt dann, dass der Shell-Prozess, den er zum Ausführen der Remote-Befehle gestartet hat, unerwartet beendet wurde, und meldet daher einen Fehler an den SSH-Client.
Antwort2
Ihr Remote-Befehl beendet sich selbst:
$ 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
Antwort3
Sie weisen pkill an, alles zu beenden, was mit „asdf“ übereinstimmt. Sie sollten es anweisen, mit [a]sdf übereinzustimmen. Auf diese Weise sucht es weiterhin nach allem mit dem Namen „asdf“, sieht sich selbst jedoch nicht (wenn Sie asdf mit [a]sdf ausrichten, beachten Sie, dass das s mit ] und nicht mit s ausgerichtet ist.)
ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'
Dies ist ein gängiger Trick, der auch bei grep/egrep/awk/usw. 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 (die immer noch lesenswert ist!)
Es ist nicht einfach, dies zu „automatisieren“, aber normalerweise können Sie jedes Mal, wenn Sie nach einer variablen Zeichenfolge „regexp=etwas“ greppen müssen, Folgendes versuchen:
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" ...