我想构建一个执行命令的bash脚本,同时执行其他操作,如果脚本被杀死,可能会杀死命令。比如说,执行大文件的cp并同时打印自复制开始以来经过的时间,但是如果脚本被杀死它也会杀死副本。 我不想要使用rsync,原因有两个:1)很慢2)我想学习如何做,它可能很有用。 我试过这个:
until cp SOURCE DEST
do
#evaluates time, stuff, commands, file dimensions, not important now
#and echoes something
done
但它不会执行do - done
块,因为它正在等待副本结束。你能建议一下吗?
答案 0 :(得分:35)
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
kill -0
检查进程是否正在运行。请注意,它实际上并不表示进程并将其终止,正如名称所暗示的那样。至少不是信号0。
答案 1 :(得分:9)
解决问题涉及三个步骤:
在后台执行命令,以便在脚本执行其他操作时继续运行。您可以通过使用&
命令执行此操作。有关详细信息,请参阅section on Job Control in the Bash Reference Manual。
跟踪该命令的状态,以便您知道它是否仍在运行。您可以使用特殊变量$!
执行此操作,该变量设置为您在后台运行的最后一个命令的PID(进程标识符),如果没有启动后台命令,则为空。 Linux为每个正在运行的进程创建一个目录/proc/$PID
,并在进程退出时删除它,因此您可以检查该目录是否存在,以确定后台命令是否仍在运行。您可以从Linux文档项目的File System Hierarchy page或Advanced Bash-Scripting Guide了解有关/proc
的更多信息。
如果您的脚本被终止,请终止后台命令。您可以使用trap
命令执行此操作,该命令为bash builtin command。
把各个部分放在一起:
# 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
请注意,我们检查/proc
目录以确定后台命令是否仍在运行,因为kill -0
如果在该进程不再存在时调用它将产生错误。
更新以解释使用trap
:
语法为trap [arg] [sigspec …]
,其中sigspec …
是要捕获的信号列表,arg
是在引发任何信号时执行的命令。在这种情况下,该命令是列表:
'[ -z $! ] || kill $!'
这是一种常见的bash习语,它利用||
的处理方式。如果cmd1 || cmd2
或cmd1
成功,则表单cmd2
的表达式将评估为成功。但是bash很聪明:如果cmd1
成功,bash知道完整的表达式也必须成功,所以它不会费心去评估cmd2
。另一方面,如果cmd1
失败,cmd2
的结果将确定表达式的整体结果。因此||
的一个重要特性是仅当cmd2
失败时才会执行cmd1
。这意味着它是(无效)序列的快捷方式:
if cmd1; then
# do nothing
else
cmd2
fi
考虑到这一点,我们可以看到
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
将测试$!
是否为空(这意味着后台任务从未执行过)。如果失败,这意味着 执行了任务,它将终止任务。
答案 2 :(得分:2)
你想要的是在shell中一次做两件事。通常的方法是使用作业。您可以通过使用&符结束命令来启动后台作业。
copy $SOURCE $DEST &
然后,您可以使用jobs
命令检查其状态。
了解更多:
答案 3 :(得分:1)
这是使用ps -p
执行该操作的最简单方法:
[command_1_to_execute] &
pid=$!
while ps -p $pid &>/dev/null; do
[command_2_to_be_executed meanwhile command_1 is running]
sleep 10
done
如果10 seconds
仍在后台运行,则每command_2
command_1
运行一次。
希望这会对你有所帮助:)