比方说,我有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
。
但是,如果你真的需要一个命令队列,你必须自己实现一个。
第一个想法是将您的队列存储在一个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 &
在后台执行命令。 那么,我们可以,但后台子进程(subshell)会收到自己的queue
变量的副本,并且产卵后所做的修改不会反映在run
子进程中。
另一种方法是在一个文件中实现你的队列,每行存储一个命令。 这实际上是可行的,但我们也需要确保一些互斥机制(例如flock
)。
虽然这种方法可行,但我不喜欢在每次需要在位置0插入命令时重写完整文件的想法。尽管如此,通过在/dev/shm
(ramdisk)上创建文件, ,但这在MacOS上不起作用。
先前方法的逻辑扩展是使用实际共享的内存队列,就像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