在命令运行时执行bash循环

时间:2013-11-23 16:49:22

标签: 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

但它不会执行do - done块,因为它正在等待副本结束。你能建议一下吗?

4 个答案:

答案 0 :(得分:35)

untilwhile相反。当另一个命令运行时,它与做事情无关。为此,您需要使用&在后​​台运行您的任务。

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)

解决问题涉及三个步骤:

  1. 在后台执行命令,以便在脚本执行其他操作时继续运行。您可以通过使用&命令执行此操作。有关详细信息,请参阅section on Job Control in the Bash Reference Manual

  2. 跟踪该命令的状态,以便您知道它是否仍在运行。您可以使用特殊变量$!执行此操作,该变量设置为您在后台运行的最后一个命令的PID(进程标识符),如果没有启动后台命令,则为空。 Linux为每个正在运行的进程创建一个目录/proc/$PID,并在进程退出时删除它,因此您可以检查该目录是否存在,以确定后台命令是否仍在运行。您可以从Linux文档项目的File System Hierarchy pageAdvanced Bash-Scripting Guide了解有关/proc的更多信息。

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

  4. 把各个部分放在一起:

    # 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 || cmd2cmd1成功,则表单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运行一次。

希望这会对你有所帮助:)