shell 脚本与 shell 中的行为不同?

shell 脚本与 shell 中的行为不同?

更新:

我已将脚本中的部分更改grep $1grep '$1'(当我试图表示时),这次我得到了grep "$1"

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

消息(而不是Terminated: 15消息)。我不明白发生了什么事。

问题:

我写了一个简单的 shell 脚本,名为mykill.

mykill

#!/bin/sh

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

然而,有一个奇怪的行为。当我写下这一行时:

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

手动在 bash 上,如果没有任何输出ps -A | grep process_name,我会得到以下内容:

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

如果出现问题,命令会正确执行并静默终止。

现在,如果我通过执行文件来运行脚本mykill,如果 的输出出现某些内容ps -A | grep process_name,则脚本会正确执行并静默终止,这与手动执行命令的行为相同。

但是,如果 的输出没有出现任何内容ps -A | grep process_name,我就不会收到有关使用kill 命令的消息。相反,我得到:

Terminated: 15

我还检查了返回代码。在我尝试通过在 shell 上手动写入命令来终止不存在的进程后,echo $?返回1。但是,在我尝试通过调用脚本来终止不存在的进程后,echo $?返回143

这里发生了什么?为什么我在通过手动在 shell 上编写的方式执行相同的命令时与在 shell 脚本中执行它时观察到不同的行为?

注意:sh我的工作 shell 都是bash.

奖励:我的 shell 脚本能否以更有效和/或更优雅的方式编写,仅使用POSIX 实用程序?如果是这样,怎么办?

答案1

可移植性说明。的输出格式ps -APOSIX 未指定对于不符合 Unix 的系统(如 FreeBSD)(您会注意到输出格式部分和选项的描述-f都被标记为XSI在规范中),所以你不能真正可靠地对其进行后处理。

例如,对于Linux 上的psfrom procps,它将输出PID TTY TIME CMDcolumns(其中CMD是进程名称,而不是命令参数),而在 FreeBSD 上它会输出PID TT STAT TIME COMMAND(其中COMMAND是参数)。

鉴于您的使用情况grep -v grep,我想您期望后者或至少ps -A输出进程执行的命令的参数,而不仅仅是进程名称(通常源自最后一个执行命令或第一个(第0)参数)。

如果您grep只想grep使用命令参数,则应该使用:

ps -A -o pid= -o args=

其输出由 POSIX 指定。

现在,你的问题是它mykill正在自杀,因为mykill foo匹配foo.

另一个问题是它mykill grep不会杀死任何东西。

在这里,你可以这样做:

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

(请注意,POSIX 不指定 POSIX 实用程序的路径,sh也不指定 she-bang 机制,因此/bin/sh可能不是 POSIX shell。但实际上,大多数 POSIX 系统都支持 she-bang,并且它可以是/bin/shPOSIXsh或 Bournesh并且上面的代码应该适用于两者)。

尽管这并不理想,因为即使没有找到进程,它也总是返回 true (0) 退出状态。更好的方法是:

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

grep -m 1在这两种情况下,我们只会按照您的方法建议的方式终止第一个匹配进程。

现在,trap '' SIGTERM我们确保我们的进程没有被杀死,如果我们要杀死它就可以了全部匹配的进程,但因为在这里,我们只杀死第一个匹配的进程,问题是第一个很可能是正在运行的进程mykill patterngrep pattern

grep -ve grep -e mykill您可以尝试比较匹配进程的进程 ID,而不是添加一些进程(这并不是万无一失的,因为它可能会排除比预期更多的进程)。

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

(请注意,$(...)read -r是 POSIX,但不是 Bourne)。

或者使用ksh93, bash, zshor yash(都不是 POSIX 命令),这是一个具有内置正则表达式匹配的 shell:

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

相关内容