如何使用&;编辑排队的进程&;在Linux/bash中?
假设我有3个进程在Linux/Shell终端中排队,方法是使用&&将各个进程的命令分开,如下所示:如何使用&;编辑排队的进程&;在Linux/bash中?,linux,bash,shell,Linux,Bash,Shell,假设我有3个进程在Linux/Shell终端中排队,方法是使用&&将各个进程的命令分开,如下所示: <command1> && <command3> && <command4> &&&& 现在,在执行process1的命令时,我想编辑队列: e、 g.1: 我想在process1和process3之间有另一个process2。使更新后的命令队列变为: <command1> && <comm
<command1> && <command3> && <command4>
&&&&
现在,在执行process1的命令时,我想编辑队列:
e、 g.1: 我想在process1和process3之间有另一个process2。使更新后的命令队列变为:
<command1> && <command2> && <command3> && <command4>
<command1> && <command4>
&&&&&&&
e、 g.2:或者可以从队列中删除进程,例如进程3。使更新后的命令队列变为:
<command1> && <command2> && <command3> && <command4>
<command1> && <command4>
&&
有没有办法动态编辑命令队列?i、 e.当process1已经被执行时?正如人们(Barmar&Patrick)已经评论的那样,当命令1正在执行时,没有办法进行操作
执行后,可以使用$检查命令的状态?(或者)您可以使用您正在寻找的任何其他逻辑。使用它可以分别构建队列
<command1>
if [ $? eq 0 ]
then
#on success
<command2> && <command3> && <command4>
else
#on failure
<command4>
fi
如果[$?等式0]
然后
#论成功
&& &&
其他的
#论失败
fi
您在bash
(非队列)中讨论的内容,一旦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 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
然后将其用于运行以下命令:
#!/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 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
但是这种方法的主要问题是您不能真正异步地修改这个队列。您可以在运行之前或在run
循环中运行时修改它,但这可能不是您想要的
你可能会想,为什么我们不能在后台用run&
执行命令呢。当然,我们可以这样做,但是后台子流程(subshell)将接收它自己的队列
变量副本,并且生成后所做的修改不会反映在运行
子流程中
尝试2:简单文件队列
另一种方法是在文件中实现队列,每行存储一条命令。这实际上是可行的,但我们还需要确保某种互斥机制到位(例如flock
)
虽然这种方法可行,但我不喜欢每次需要在位置0插入命令时都重写整个文件的想法。不过,您可以通过在/dev/shm
(ramdisk)上创建文件来将其保存在内存中,但这在MacOS上不起作用
尝试3:Redis队列
前一种方法的逻辑扩展是使用实际的共享内存队列,如提供的队列
bash
此基于Redis的队列的包装器为:
#!/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
}
您可以运行第一个示例(在上一个命令运行时插入),如下所示:
#!/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 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
第二个示例(删除命令):
产出:
command1
command4
不要在shell中运行
command1&&command2&&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\u命令
无法做到这一点。没有可操作的队列。请实现您自己的队列管理器,该管理器在分析最后一个终止队列的退出状态后手动启动进程,而不是使用&&
。Barmar,为了更好地了解情况,请您将此作为答案而不是注释发布。您可以查看execline
的工作原理。如果一个程序自己负责执行这些事情,那么它可以控制之后发生的事情。(而且完全有可能编写一种图灵完整语言来表达这种约束。我在下面的回答中使用了硬编码符号,这是在欺骗,降低了灵活性,但这样的攻击是没有必要的——例如,使用长度前缀机制,可以使该机制在构建时对程序内容没有任何约束;而不是在子参数前面加前缀的execline方法也是可行的。)command1;if[$?-eq 0];then foo;else bar;fi
最好写为if command1;then foo;else bar;fi
。感谢您的输入和宝贵的评论