如何编辑在Linux / bash中使用&&排队的进程?

比方说,我有3个进程在Linux / Shellterminal排队,通过使用&&来分隔各个进程的命令,如下所示:

<command1> && <command3> && <command4> 

现在执行process1的命令时,我想编辑队列:


例如1:我想在process1和process3之间有另一个process2。 所以更新的命令队列变成:

 <command1> && <command2> && <command3> && <command4> 

例如2:或者可以从队列中去除例如process3的进程。 所以更新的命令队列变成:

 <command1> && <command4> 

有没有办法编辑命令队列? 即当process1已被执行?

你正在讨论命令列表 (不是队列),一旦shell解析它,你就不能修改它。

从这个问题来看,你是否真的需要一个队列,或者一个简单的退出状态测试会为你服务,例如:

 #!/bin/bash if cmd1; then cmd2 else if cmd3; then cmd4 fi fi 

(这将首先运行cmd1 ,然后根据它的成功,运行cmd2如果运行成功,或者尝试运行cmd3 ,然后运行cmd4 ,最后一个if程序块可以简化为cmd3 && cmd4

但是,如果你真的需要一个命令队列,你必须自己实现一个。

试试1:简单的数组队列

第一个想法是将您的队列存储在一个shell数组变量中,如下所示:

 #!/bin/bash declare -a queue clear() { queue=(); } is_empty() (( ! ${#queue[@]} )) # insert an item at the beginning insert() { queue=("$1" "${queue[@]}") } # append an item at the end push() { queue=("${queue[@]}" "$1") } # remove an item from the beginning pop() { queue=("${queue[@]:1}") } # return the first item, without popping it peek() { echo "${queue[0]}" } 

然后用它来运行这样的命令:

 # run commands from the `queue` run() { while ! is_empty; do local cmd=$(peek) pop eval "$cmd" || return done return 0 } # run: echo before && sleep 1 && echo after clear push 'echo before' push 'sleep 1' push 'echo after' run 

但是这种方法的主要问题是你不能异步修改这个队列 。 您可以在运行之前或在运行循环内run时对其进行修改,但这可能不是您所要求的。

你可能会想,为什么我们不能用run &在后台执行命令。 那么,我们可以,但后台子进程(subshel​​l)会收到自己的queue变量的副本,并且产卵后所做的修改不会反映在run子进程中。

试试2:简单的文件队列

另一种方法是在一个文件中实现你的队列,每行存储一个命令。 这实际上是可行的,但我们也需要确保一些互斥机制(例如flock )。

虽然这种方法可行,但我不喜欢在每次需要在位置0插入命令时重写完整文件的想法。尽管如此,通过在/dev/shm (ramdisk)上创建文件, ,但这在MacOS上不起作用。

试试3:Redis队列

先前方法的逻辑扩展是使用实际共享的内存队列,就像Redis提供的那样。

这个基于Redis的队列的bash包装将是:

 #!/bin/bash redis=(redis-cli --raw) clear() { "${redis[@]}" DEL queue &>/dev/null } is_empty() (( $("${redis[@]}" LLEN queue) == 0 )) # insert the item at the beginning insert() { "${redis[@]}" LPUSH queue "$1" &>/dev/null } # append the item at the end push() { "${redis[@]}" RPUSH queue "$1" &>/dev/null } # remove the item from the beginning pop() { "${redis[@]}" LPOP queue } peek() { "${redis[@]}" LRANGE queue 0 0 } show() { "${redis[@]}" LRANGE queue 0 -1 } 

你可以运行你的第一个例子(在前一个命令运行时插入),如下所示:

 # run commands from the redis queue run() { while ! is_empty; do eval "$(pop)" || return done return 0 } # start with: echo before && sleep 1 && echo after clear push 'echo before' push 'sleep 3' push 'echo after' run & # but then, 1sec after running, modify the queue, insert another command sleep 1 insert 'echo inserted' wait 

示例输出:

 $ ./redis-queue-demo.sh before inserted after 

你的例子的解决方案

所以,使用Redis方法,你的第一个例子 (插入一个命令)看起来像:

 clear push 'echo command1; sleep 2' push 'echo command3' push 'echo command4' run & sleep 1 insert 'echo command2' wait 

输出:

 command1 command2 command3 command4 

第二个例子 (删除命令):

 clear push 'echo command1; sleep 2' push 'echo command3' push 'echo command4' run & sleep 1 pop >/dev/null wait 

输出:

 command1 command4 

不要在shell中运行command1 && command2 && command3 ,而要将逻辑移到决定运行的命令中 。 从而:

 command1 --c1-arg1 --c1-arg2 -- command2 --c2-arg1 -- command3 

…每个命令的逻辑类似于:

 command1() { local arg1=0 arg2=0 local -a next_command=( ) extra_args=( ) while (( $# )); do case $1 in --c1-arg1) arg1=1 ;; # example --c1-arg2) arg2=1 ;; --) shift; next_command=( "$@" ); break; esac shift done # ...now, let's say we wish to conditionally add a comand4 to the end of the list: if (( arg1 && arg2 )); then if (( ${#next_command[@]} )); then next_command+=( -- ) next_command+=( command4 ) fi # regardless, shift control to our next command if it exists (( ${#next_command[@]} )) && exec "${next_command[@]}" } 

这样,你不会试图让你的命令修改shell(它的父进程)的状态,而只是让命令修改自己的变量 ,即数组next_command

正如人们(Barmar&Patrick)已经评论过的那样,当命令1执行时没有任何操作的方法。

执行完后可以使用$检查命令的状态吗? (或)您可以使用您正在寻找的任何其他逻辑。 使用它可以分别建立你的队列。

 <command1> if [ $? eq 0 ] then #on success <command2> && <command3> && <command4> else #on failure <command4> fi