![OR con verdadero en un comando sobre ssh](https://rvso.com/image/122433/OR%20con%20verdadero%20en%20un%20comando%20sobre%20ssh.png)
Cuando intento ejecutar pkill -f
de forma remota a través de ssh e intento descartar el posible código de error (para continuar con el resto de mi script incluso si no se encuentra ningún proceso), || true
no se comporta como esperaba.
$ 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
Supongo que esssheso devuelve 255, no el comando entre comillas, pero ¿por qué?
Respuesta1
Su suposición de que es ssh
él mismo el que devuelve el estado de salida 255 es correcta. La ssh
página de manual establece que:
ssh sale con el estado de salida del comando remoto o con 255 si ocurrió un error.
Si simplemente ejecutara , lo más probable es que obtendría un estado de salida de , correspondiente al estado de "ssh [email protected] "pkill -f asdf"
1
pkill
Ningún proceso coincide”.
La parte desafiante es entenderpor quése produce un error con SSH al ejecutar
ssh [email protected] "pkill -f asdf || true"
Comandos remotos SSH
El servidor SSH inicia un shell para ejecutar comandos remotos. Aquí hay un ejemplo de esto en acción:
$ 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
Tenga en cuenta que el shell predeterminado es bash
y que el comando remoto no es un comando simple sino untubería, “una secuencia de uno o más comandos separados por el operador de control |
”.
El shell Bash es lo suficientemente inteligente como para darse cuenta de que si el comando que le pasa la -c
opción es uncomando simple, se puede optimizar sin bifurcar un nuevo proceso, es decir, directamente exec
con el comando simple en lugar de realizar el paso adicional de fork
ing antes de hacerlo exec
. A continuación se muestra un ejemplo de lo que sucede cuando ejecuta un comando simple remoto ( ps -elf
en este caso):
$ 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
Me encontré con este comportamiento antes pero no pude encontrar una referencia mejor que no seaesta respuesta de AskUbuntu.
comportamiento pkill
Dado que pkill -f asdf || true
no es un comando simple (es unlista de comandos), la optimización anterior no puede ocurrir, por lo que cuando se ejecuta , el proceso se bifurca y se ejecuta .ssh [email protected] "pkill -f asdf || true"
sshd
bash -c "pkill -f asdf || true"
Como señala la respuesta de ctx, pkill
no eliminará su propio proceso. De todos modos, esovoluntadelimine cualquier otro proceso cuya línea de comando coincida con el -f
patrón. El bash -c
comando coincide con este patrón, por lo que finaliza este proceso: su propio padre (como sucede).
Luego, el servidor SSH ve que el proceso de shell que inició para ejecutar los comandos remotos se eliminó inesperadamente, por lo que informa un error al cliente SSH.
Respuesta2
Su comando remoto se suicida:
$ 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 y pkill ignorarán su propio proceso, pero con el indicador -f, encontrarán el shell principal:
$ 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
Respuesta3
Le pides a pkill que elimine cualquier cosa que coincida con "asdf". Deberías decirle que coincida con [a]sdf, de esa manera seguirá buscando cualquier cosa llamada "asdf", pero no se verá a sí mismo (si alineas asdf con [a]sdf, observa que la s está alineada con ] y no s.)
ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'
Es un truco común que también se usa con grep/egrep/awk/etc:
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
Este truco es antiguo y lo vi hace décadas en las preguntas frecuentes de Unix (¡que todavía es una buena lectura!)
"Automatizarlo" no es fácil, pero normalmente cada vez que necesitas buscar una cadena variable regexp="algo", puedes intentar hacer:
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" ...