![ORing com true em um comando sobre ssh](https://rvso.com/image/122433/ORing%20com%20true%20em%20um%20comando%20sobre%20ssh.png)
Quando tento rodar pkill -f
remotamente via ssh, e tento descartar o possível código de erro (para continuar com o resto do meu script mesmo que nenhum processo seja encontrado), || true
não se comporta como esperado.
$ 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
Suponho que sejasshque retorna 255, não o comando entre aspas, mas por quê?
Responder1
Sua suposição de que é ssh
ele quem retorna o status de saída 255 está correto. A ssh
página de manual afirma que:
ssh sai com o status de saída do comando remoto ou com 255 se ocorreu um erro.
Se você simplesmente executasse , provavelmente obteria um status de saída de , correspondente ao status de “ssh [email protected] "pkill -f asdf"
1
pkill
Nenhum processo correspondente”.
A parte desafiadora é entenderpor queocorre um erro com SSH quando você executa
ssh [email protected] "pkill -f asdf || true"
Comandos remotos SSH
O servidor SSH inicia um shell para executar comandos remotos. Aqui está um exemplo disso em ação:
$ 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
Observe que o shell padrão é bash
e que o comando remoto não é um comando simples, mas umgasoduto, “uma sequência de um ou mais comandos separados pelo operador de controle |
”.
O shell Bash é inteligente o suficiente para perceber que se o comando que está sendo passado para ele pela -c
opção for umcomando simples, ele pode otimizar não bifurcando realmente um novo processo, ou seja, é diretamente exec
o comando simples, em vez de passar pela etapa extra de fork
ing antes dele exec
. Aqui está um exemplo do que acontece quando você executa um comando simples remoto ( ps -elf
neste 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
Já me deparei com esse comportamento antes, mas não consegui encontrar uma referência melhor além deesta resposta do AskUbuntu.
comportamento pkill
Como pkill -f asdf || true
não é um comando simples (é umlista de comandos), a otimização acima não pode ocorrer, portanto, quando você executa , o processo se bifurca e executa .ssh [email protected] "pkill -f asdf || true"
sshd
bash -c "pkill -f asdf || true"
Como aponta a resposta do ctx, pkill
não matará seu próprio processo. no entantovaimate qualquer outro processo cuja linha de comando corresponda ao -f
padrão. O bash -c
comando corresponde a esse padrão, então mata esse processo – seu próprio pai (na medida em que acontece).
O servidor SSH então vê que o processo shell iniciado para executar os comandos remotos foi encerrado inesperadamente e relata um erro ao cliente SSH.
Responder2
Seu comando remoto se mata:
$ 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 e pkill irão ignorar seu próprio processo, mas com o sinalizador -f, eles encontrarão o shell pai:
$ 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
Responder3
Você pede ao pkill para matar qualquer coisa que corresponda a "asdf". Você deve dizer a ele para corresponder a [a]sdf, dessa forma ele ainda procurará por qualquer coisa chamada "asdf", mas não se verá (se você alinhar asdf com [a]sdf, observe que s está alinhado com ] e não é.)
ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'
É um truque comum também usado com 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
Esse truque é antigo e eu o vi há décadas nas perguntas frequentes do Unix (que ainda é uma boa leitura!)
Para "automatizá-lo" não é fácil, mas geralmente toda vez que você precisa usar o grep para uma variável string regexp="something", você pode tentar fazer:
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" ...