在命令运行时执行bash循环

mar*_*rco 19 bash shell

我想构建一个执行命令的bash脚本,同时执行其他操作,如果脚本被杀死,可能会杀死命令.比如说,执行大文件的cp并同时打印自复制开始以来经过的时间,但是如果脚本被杀死它也会杀死副本.我并不想用rsync的,原因有二:1)是缓慢的,2)我想学习如何做到这一点,它可能是有用的.我试过这个:

until cp SOURCE DEST
do
#evaluates time, stuff, commands, file dimensions, not important now
#and echoes something
done
Run Code Online (Sandbox Code Playgroud)

但是它没有执行do - done块,因为它正在等待副本结束.你能建议一下吗?

Joh*_*ica 36

until与...相反while.当另一个命令运行时,它与做事情无关.为此,您需要在后台运行您的任务&.

cp SOURCE DEST &
pid=$!

# If this script is killed, kill the `cp'.
trap "kill $pid 2> /dev/null" EXIT

# While copy is running...
while kill -0 $pid 2> /dev/null; do
    # Do stuff
    ...
    sleep 1
done

# Disable the trap on a normal exit.
trap - EXIT
Run Code Online (Sandbox Code Playgroud)

kill -0检查进程是否正在运行.请注意,它实际上并不表示进程并将其终止,正如名称所暗示的那样.至少不是信号0.

  • 很棒的答案 - 不知道`kill -0`.将`2>/dev/null`附加到`kill -0 $ pid`以禁止在进程不再存在时发出的错误消息. (2认同)

Ada*_*iss 11

解决问题涉及三个步骤:

  1. 在后台执行命令,因此当脚本执行其他操作时它将继续运行.您可以通过执行以下命令来执行此操作&.有关详细信息,请参阅Bash参考手册中有关作业控制部分.

  2. 跟踪该命令的状态,以便您知道它是否仍在运行.您可以使用特殊变量执行此操作,该变量$!设置为您在后台运行的最后一个命令的PID(进程标识符),如果没有启动后台命令,则为空.Linux /proc/$PID为每个正在运行的进程创建一个目录,并在进程退出时删除它,因此您可以检查该目录是否存在,以确定后台命令是否仍在运行.您可以/proc从Linux Documentation Project的文件系统层次结构页面Advanced Bash-Scripting Guide中了解更多您想知道的内容.

  3. 如果您的脚本被终止,请终止后台命令.您可以使用trap命令执行此操作,该命令是bash内置命令.

把碎片放在一起:

# Look for the 4 common signals that indicate this script was killed.
# If the background command was started, kill it, too.
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
cp $SOURCE $DEST &  # Copy the file in the background.
# The /proc directory exists while the command runs.
while [ -e /proc/$! ]; do
    echo -n "."  # Do something while the background command runs.
    sleep 1  # Optional: slow the loop so we don't use up all the dots.
done
Run Code Online (Sandbox Code Playgroud)

请注意,我们检查/proc目录以查明后台命令是否仍在运行,因为kill -0如果在该进程不再存在时调用它将生成错误.


更新解释使用trap:

语法是trap [arg] [sigspec …],其中sigspec …是要捕获的信号列表,并且arg是在引发任何这些信号时执行的命令.在这种情况下,命令是一个列表:

'[ -z $! ] || kill $!'
Run Code Online (Sandbox Code Playgroud)

这是一种常见的bash习语,它利用了||处理方式.cmd1 || cmd2如果cmd1OR cmd2成功,表单的表达式将评估为成功.但是bash很聪明:如果cmd1成功,bash知道完整的表达式也必须成功,所以它不需要去评估cmd2.另一方面,如果cmd1失败,则结果cmd2确定表达式的整体结果.因此,一个重要的特征 ||cmd2只有在cmd1失败时才会执行.这意味着它是(无效)序列的快捷方式:

if cmd1; then
   # do nothing
else
   cmd2
fi
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,我们可以看到

trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
Run Code Online (Sandbox Code Playgroud)

将测试是否$!为空(这意味着后台任务从未执行过).如果失败,这意味着任务执行,则会终止任务.