ORing com true em um comando sobre ssh

14

Quando tento executar pkill -f remotamente via ssh e tentar descartar o possível código de erro (para continuar com o restante 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 seja ssh que retorne 255, não o comando entre aspas, mas por quê?

    
por Gauthier 24.11.2017 / 11:06

3 respostas

29

Sua suposição de que é ssh em si que retorna o status de saída 255 está correta. A página ssh man afirma que:

ssh exits with the exit status of the remote command or with 255 if an error occurred.

Se você simplesmente executasse ssh [email protected] "pkill -f asdf" , provavelmente obteria um status de saída de 1 , correspondente ao status pkill para " Nenhum processo correspondido ".

A parte desafiadora é entender por que um erro ocorre com o SSH quando você executa

ssh [email protected] "pkill -f asdf || true"

Comandos remotos SSH

O servidor SSH inicia um shell para executar o (s) comando (s) remoto (s). Veja 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 um pipeline ," uma sequência de um ou mais comandos separados pelo operador de controle | ”.

O shell Bash é inteligente o bastante para perceber que se o comando que está sendo passado para ele pela opção -c é um comando simples , ele pode otimizar por não realmente bifurcar um novo processo, isto é, diretamente exec s o comando simples em vez de passar pela etapa extra de fork ing antes de exec s. Veja um exemplo do que acontece quando você executa um comando simples e 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

Eu já vi esse comportamento antes, mas não consegui encontrar uma referência melhor do que esta resposta do AskUbuntu .

comportamento pkill

Como pkill -f asdf || true não é um comando simples (é uma lista de comandos ) , a otimização acima não pode ocorrer, portanto, quando você executar ssh [email protected] "pkill -f asdf || true" , os sshd process forks e execs bash -c "pkill -f asdf || true" .

Como a resposta do ctx aponta, pkill não mata seu próprio processo. No entanto, ele irá matar qualquer outro processo cuja linha de comando corresponda ao padrão -f . O comando bash -c corresponde a esse padrão para que ele mate esse processo - seu próprio pai (como acontece).

O servidor SSH, em seguida, vê que o processo de shell iniciado para executar os comandos remotos foi morto inesperadamente, de modo que ele relata um erro para o cliente SSH.

    
por 24.11.2017 / 13:07
9

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
    
por 24.11.2017 / 11:45
3

Você pede ao pkill para matar qualquer coisa que combine com "asdf". Você deve dizer para coincidir com [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 os s estão alinhados com] e não s.)

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

É um truque comum usado também com o 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 vi isso décadas atrás no faq do Unix (que ainda é uma boa leitura!)

Para "automatizar" isso, não é fácil, mas normalmente cada vez que você precisar de uma variável de string regexp="something", você pode tentar:

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" ...
    
por 24.11.2017 / 17:36

Tags