
ACTUALIZAR:
Cambié la grep $1
parte 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: 15
mensaje). 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 mykill
archivo, 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 sh
y 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 -A
esno 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 -f
opció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 ps
from procps
en Linux, generará PID TTY TIME CMD
columnas (donde CMD
está el nombre del proceso, no los argumentos del comando), mientras que en FreeBSD generará PID TT STAT TIME COMMAND
( COMMAND
siendo los argumentos).
Dado su uso de grep -v grep
, supongo que espera lo último o al menos que ps -A
genere 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 grep
intención es grep
utilizar únicamente los argumentos del comando, debe usar:
ps -A -o pid= -o args=
cuya salida está especificada por POSIX.
Ahora, tu problema es que mykill
se está matando porque las mykill foo
coincidencias foo
.
Otro problema es que mykill grep
no 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 sh
utilidad POSIX ni el mecanismo she-bang, por lo que /bin/sh
puede 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/sh
es un POSIX sh
o Bourne sh
y 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 1
enfoque sugiere que desea hacerlo.
Ahora, trap '' SIGTERM
nos 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 pattern
o 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 -r
son 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:zsh
yash
#! /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
}