ORing mit true in einem Befehl über SSH

ORing mit true in einem Befehl über SSH

Wenn ich versuche, die Ausführung pkill -fremote über SSH durchzuführen und den möglichen Fehlercode zu verwerfen (um mit dem Rest meines Skripts fortzufahren, auch wenn kein Prozess gefunden wird), || trueverhä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 sshselbst den Exit-Status 255 zurückgibt, ist richtig. Auf der sshManpage 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"1pkillKeine 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 bashund 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 -cum eineneinfacher Befehl, kann es optimieren, indem es nicht wirklich einen neuen Prozess aufspaltet, d. h. es execführt den einfachen Befehl direkt aus, anstatt den zusätzlichen Schritt des forkAusführens auszuführen, bevor es execausgeführt wird. Hier ist ein Beispiel dafür, was passiert, wenn Sie einen einfachen Remotebefehl 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 bin schon einmal auf dieses Verhalten gestoßen, konnte aber keine bessere Referenz finden alsdiese AskUbuntu-Antwort.

pkill-Verhalten

Da pkill -f asdf || trueist 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"sshdbash -c "pkill -f asdf || true"

Wie die Antwort von ctx zeigt, pkillwird es seinen eigenen Prozess nicht beenden. EsWilleBeenden Sie alle anderen Prozesse, deren Befehlszeile dem -fMuster entspricht. Der bash -cBefehl 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" ...

verwandte Informationen