Comportamento diferente no shell script e no shell?

Comportamento diferente no shell script e no shell?

ATUALIZAR:

Eu mudei a grep $1parte para grep '$1'(enquanto tentava dizer grep "$1") no roteiro e desta vez consegui o

kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

mensagem (em vez da Terminated: 15mensagem). Eu não entendo o que está acontecendo.

PERGUNTA:

Eu escrevi um script de shell simples chamado mykill.

mykill:

#!/bin/sh

kill `ps -A | grep $1 | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`

No entanto, há um comportamento estranho. Quando escrevo a linha:

kill `ps -A | grep process_name | grep -v 'grep' | grep -Eom 1 '^[0-9]+'`

manualmente no bash, se nada aparecer como saída ps -A | grep process_name, recebo o seguinte:

kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

Se algo acontecer, o comando será executado corretamente e terminará silenciosamente.

Agora, se eu executar o script executando o mykillarquivo, se algo surgir como saída de ps -A | grep process_name, o script será executado corretamente e terminará silenciosamente, que é o mesmo comportamento de executar o comando manualmente.

Mas se nada aparecer como resultado de ps -A | grep process_name, não recebo a mensagem sobre o uso do comando kill. Em vez disso, recebo:

Terminated: 15

Também verifiquei os códigos de retorno. Depois de tentar encerrar um processo inexistente escrevendo manualmente o comando no shell, echo $?1. Porém, depois de tentar encerrar um processo inexistente chamando o script, echo $?143.

O que está acontecendo aqui? Por que estou observando comportamentos diferentes ao executar o mesmo comando escrevendo-o manualmente no shell, em vez de executá-lo em um script de shell?

NOTA: Ambos she meu shell de trabalho são bash.

BÔNUS: Meu shell script poderia ser escrito de uma forma mais eficiente e/ou elegante, usando apenasUtilitários POSIX? Se sim, como?

Responder1

Uma nota de portabilidade. O formato de saída para ps -Aénão especificado pelo POSIXpara sistemas não compatíveis com Unix (como o FreeBSD) (você notará que as seções do formato de saída e a descrição da -fopção estão todas marcadasXSIna especificação), então você não pode pós-processá-lo de maneira confiável e portátil.

Por exemplo, com psfrom procpsno Linux, ele produzirá PID TTY TIME CMDcolunas (onde CMDestá o nome do processo, não os argumentos do comando), enquanto no FreeBSD ele produzirá PID TT STAT TIME COMMAND( COMMANDsendo os argumentos).

Dado o seu uso de grep -v grep, suponho que você esteja esperando o último ou pelo menos que ps -Aproduza os argumentos do comando que o processo executou, em vez de apenas o nome do processo (geralmente derivado do nome do arquivo do último comando de execução ou do primeiro ( 0º ) argumento).

Se você greppretende grepapenas usar argumentos de comando, você deve usar:

ps -A -o pid= -o args=

cuja saída é especificada por POSIX.

Agora, o seu problema é que mykillestá se matando por causa dos mykill foofósforos foo.

Outro problema é que mykill grepnão mataria nada.

Aqui você poderia fazer:

#! /bin/sh -
PATTERN=${1?} export PATTERN
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | awk '$0 ~ ENVIRON["PATTERN"] {
  system("kill " $1); exit}'

(observe que o POSIX não especifica o caminho do shutilitário POSIX nem o mecanismo she-bang, portanto /bin/shpode não ser um shell POSIX. Na prática, porém, she-bang é suportado na maioria dos sistemas POSIX e /bin/shé um POSIX shou Bourne she o código acima deve funcionar em ambos).

Embora isso não seja ideal, pois sempre retorna um status de saída verdadeiro (0), mesmo quando nenhum processo é encontrado. Uma abordagem melhor seria:

#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
ps -A -o pid= -o args= | grep -e "$pattern" | {
  read pid args && kill "$pid"
}

Em ambos os casos, eliminamos apenas o primeiro processo de correspondência conforme sua grep -m 1abordagem sugere que você deseja fazer.

Agora, trap '' SIGTERMgarantimos que nossos processos não sejam eliminados, o que seria bom se matássemostodosos processos de correspondência, mas como aqui estamos apenas eliminando o primeiro correspondente, o problema é que esse primeiro pode muito bem ser aquele em execução mykill patternou grep pattern.

Em vez de adicionar alguns grep -ve grep -e mykill(o que não seria infalível, pois poderia excluir mais processos do que o pretendido), você poderia tentar comparar os IDs dos processos correspondentes.

#! /bin/sh -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
             # just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | grep -e "$pattern" | {
  while read -r pid ppid args; do
    if [ "$pid" -ne "$$" ] && [ "$ppid" -ne "$$" ]; then
      kill "$pid"
      exit # with the exit status of kill above
    fi
  done
  exit 1 # not found
}

(observe que $(...)e read -rsão POSIX, mas não Bourne).

Ou usando ksh93, bash, zshou yash(nenhum dos quais são comandos POSIX), que é um shell com correspondência de expressão regular integrada:

#! /bin/bash -
pattern=${1?}
trap '' TERM # ignore SIGTERM for the shell and its children
             # just in case
psoutput=$(exec ps -A -o pid= -o ppid= -o args=)
printf '%s\n' "$psoutput" | {
  while read -r pid ppid args; do
    if ((pid != $$ && ppid != $$)) && [[ $args =~ $pattern ]]; then
      kill "$pid"
      exit # with the exit status of kill above
    fi
  done
  exit 1 # not found
}

informação relacionada