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

如何使用&;编辑排队的进程&;在Linux/bash中?,linux,bash,shell,Linux,Bash,Shell,假设我有3个进程在Linux/Shell终端中排队,方法是使用&&将各个进程的命令分开,如下所示: <command1> && <command3> && <command4> &&&& 现在,在执行process1的命令时,我想编辑队列: e、 g.1: 我想在process1和process3之间有另一个process2。使更新后的命令队列变为: <command1> && <comm

假设我有3个进程在Linux/Shell终端中排队,方法是使用&&将各个进程的命令分开,如下所示:

<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
。感谢您的输入和宝贵的评论