¿Comportamiento diferente en el script de Shell versus en Shell?

¿Comportamiento diferente en el script de Shell versus en Shell?

ACTUALIZAR:

Cambié la grep $1parte a grep '$1'(mientras intentaba decir grep "$1") en el guión y esta vez obtuve la

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

mensaje (en lugar del Terminated: 15mensaje). No entiendo lo que está pasando.

PREGUNTA:

He escrito un script de shell simple llamado mykill.

mykill:

#!/bin/sh

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

Sin embargo, hay un comportamiento extraño. Cuando escribo la línea:

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

manualmente en bash, si no aparece nada como resultado de ps -A | grep process_name, obtengo lo siguiente:

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

Si surge algo, el comando se ejecuta correctamente y finaliza silenciosamente.

Ahora, si ejecuto el script ejecutando el mykillarchivo, si aparece algo como resultado de ps -A | grep process_name, el script se ejecuta correctamente y finaliza silenciosamente, que es el mismo comportamiento que ejecutar el comando manualmente.

Pero si no aparece nada como resultado de ps -A | grep process_name, no recibo el mensaje sobre el uso del comando kill. En cambio, obtengo:

Terminated: 15

También revisé los códigos de retorno. Después de intentar finalizar un proceso inexistente escribiendo manualmente el comando en el shell, echo $?aparece 1. Sin embargo, después de intentar finalizar un proceso inexistente llamando al script, echo $?aparece 143.

¿Que está pasando aqui? ¿Por qué observo diferentes comportamientos al ejecutar el mismo comando escribiéndolo manualmente en el shell, en lugar de ejecutarlo dentro de un script de shell?

NOTA: Ambos shy mi shell de trabajo son bash.

BONIFICACIÓN: ¿Podría escribirse mi script de shell de una manera más eficiente y/o elegante, usando soloUtilidades POSIX? ¿Si es así, cómo?

Respuesta1

Una nota de portabilidad. El formato de salida para ps -Aesno especificado por POSIXpara sistemas que no son compatibles con Unix (como FreeBSD) (notarás que las secciones de formato de salida y la descripción de la -fopción están todas etiquetadasXSIen la especificación), por lo que realmente no se puede posprocesar de manera confiable y portátil.

Por ejemplo, con psfrom procpsen Linux, generará PID TTY TIME CMDcolumnas (donde CMDestá el nombre del proceso, no los argumentos del comando), mientras que en FreeBSD generará PID TT STAT TIME COMMAND( COMMANDsiendo los argumentos).

Dado su uso de grep -v grep, supongo que espera lo último o al menos que ps -Agenere los argumentos del comando que ejecutó el proceso en lugar de solo el nombre del proceso (generalmente derivado del nombre de archivo del último comando ejecutado o del primero ( 0º ) argumento).

Si su grepintención es greputilizar únicamente los argumentos del comando, debe usar:

ps -A -o pid= -o args=

cuya salida está especificada por POSIX.

Ahora, tu problema es que mykillse está matando porque las mykill foocoincidencias foo.

Otro problema es que mykill grepno mataría nada.

Aquí podrías hacer:

#! /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}'

(tenga en cuenta que POSIX no especifica la ruta de la shutilidad POSIX ni el mecanismo she-bang, por lo que /bin/shpuede no ser un shell POSIX. Sin embargo, en la práctica, she-bang es compatible con la mayoría de los sistemas POSIX y /bin/shes un POSIX sho Bourne shy el código anterior debería funcionar en ambos).

Aunque eso no es ideal ya que siempre devuelve un estado de salida verdadero (0) incluso cuando no se encuentra ningún proceso. Un mejor enfoque sería:

#! /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"
}

En ambos casos, solo finalizamos el primer proceso de coincidencia según su grep -m 1enfoque sugiere que desea hacerlo.

Ahora, trap '' SIGTERMnos aseguramos de que nuestros procesos no se eliminen, lo cual estaría bien si lo matáramos.todolos procesos coincidentes, pero dado que aquí solo matamos el primero que coincide, el problema es que ese primero puede ser el que se ejecuta mykill patterno grep pattern.

En lugar de agregar algunos grep -ve grep -e mykill(lo que no sería infalible ya que podría excluir más procesos de los previstos), puede intentar comparar los ID de los procesos coincidentes.

#! /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
}

(tenga en cuenta que $(...)y read -rson POSIX pero no Bourne).

O usando ksh93, o bash( ninguno de los cuales son comandos POSIX), que es un shell con coincidencia de expresiones regulares incorporada:zshyash

#! /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
}

información relacionada