在命令运行时执行 bash 循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20165057/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:27:04  来源:igfitidea点击:

executing bash loop while command is running

bashshell

提问by marco

I want to build a bash script that executes a command and in the meanwhile performs other stuff, with the possibility of killing the command if the script is killed. Say, executes a cp of a large file and in the meanwhile prints the elapsed time since copy started, but if the script is killed it kills also the copy. I don'twant to use rsync, for 2 reasons: 1) is slow and 2) I want to learn how to do it, it could be useful. I tried this:

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

until cp SOURCE DEST
do
#evaluates time, stuff, commands, file dimensions, not important now
#and echoes something
done

but it doesn't execute the do - doneblock, as it is waiting that the copy ends. Could you please suggest something?

但它不执行该do - done块,因为它正在等待复制结束。你能提出一些建议吗?

回答by John Kugelman

untilis the opposite of while. It's nothing to do with doing stuff while another command runs. For that you need to run your task in the background with &.

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 -0checks if a process is running. Note that it doesn't actually signal the process and kill it, as the name might suggest. Not with signal 0, at least.

kill -0检查进程是否正在运行。请注意,正如名称所暗示的那样,它实际上并没有向进程发出信号并杀死它。至少不是信号 0。

回答by Adam Liss

There are three steps involved in solving your problem:

解决您的问题涉及三个步骤:

  1. Execute a command in the background,so it will keep running while your script does something else. You can do this by following the command with &. See the section on Job Control in the Bash Reference Manualfor more details.

  2. Keep track of that command's status,so you'll know if it is still running. You can do this with the special variable $!, which is set to the PID (process identifier) of the last command you ran in the background, or empty if no background command was started. Linux creates a directory /proc/$PIDfor every process that is running and deletes it when the process exits, so you can check for the existence of that directory to find out if the background command is still running. You can learn more than you ever wanted to know about /procfrom the Linux Documentation Project's File System Hierarchy pageor Advanced Bash-Scripting Guide.

  3. Kill the background commandif your script is killed. You can do this with the trapcommand, which is a bash builtin command.

  1. 在后台执行命令,以便在您的脚本执行其他操作时它会继续运行。您可以按照带有 的命令执行此操作&。有关更多详细信息,请参阅Bash 参考手册中有关作业控制部分

  2. 跟踪该命令的状态,以便您知道它是否仍在运行。您可以使用特殊变量来执行此操作,该变量$!设置为您在后台运行的最后一个命令的 PID(进程标识符),如果没有启动后台命令,则设置为空。Linux/proc/$PID为每个正在运行的进程创建一个目录,并在进程退出时将其删除,因此您可以检查该目录是否存在,以确定后台命令是否仍在运行。您可以/proc从 Linux 文档项目的文件系统层次结构页面高级 Bash 脚本指南中学到比以往任何时候都想知道的更多。

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

Putting the pieces together:

将各个部分放在一起:

# 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

Note that we check the /procdirectory to find out if the background command is still running, because kill -0will generate an error if it's called when the process no longer exists.

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



Update to explain the use of trap:

更新以解释使用trap

The syntax is trap [arg] [sigspec …], where sigspec …is a list of signals to catch, and argis a command to execute when any of those signals is raised. In this case, the command is a list:

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

'[ -z $! ] || kill $!'

This is a common bash idiom that takes advantage of the way ||is processed. An expression of the form cmd1 || cmd2will evaluate as successful if either cmd1OR cmd2succeeds. But bash is clever: if cmd1succeeds, bash knows that the complete expression must also succeed, so it doesn't bother to evaluate cmd2. On the other hand, if cmd1fails, the result of cmd2determines the overall result of the expression. So an important feature of ||is that it will execute cmd2only if cmd1fails.That means it's a shortcut for the (invalid) sequence:

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

if cmd1; then
   # do nothing
else
   cmd2
fi

With that in mind, we can see that

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

trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM

will test whether $!is empty (which means the background task was never executed). If that fails, which means the task wasexecuted, it kills the task.

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

回答by Nick Vladiceanu

here is the simplest way to do that using ps -p:

这是使用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

This will run every 10 secondsthe command_2if the command_1is still running in background .

这将运行每一个10 secondscommand_2,如果command_1仍在后台运行。

hope this will help you :)

希望能帮到你 :)

回答by C. Ross

What you want is to do two things at once in shell. The usual way to do that is with a job. You can start a background job by ending the command with an ampersand.

你想要的是在 shell 中同时做两件事。通常的做法是找一份工作。您可以通过以与号结束命令来启动后台作业。

copy $SOURCE $DEST & 

You can then use the jobscommand to check its status.

然后您可以使用该jobs命令检查其状态。

Read more:

阅读更多: