在单行 bash 循环中使用 &(与号)

ljs*_*dev 78 command-line bash process shell-script

我一直在成功使用此命令,它更改了配置文件中的变量,然后在循环中执行 Python 脚本:

for((i=114;i<=255;i+=1)); do echo $i > numbers.txt; python DoMyScript.py; done
Run Code Online (Sandbox Code Playgroud)

由于每个DoMyScript.py实例在终止前运行大约需要 30 秒,我想将它们降级到后台,同时可以生成下一个。

我已经尝试了我熟悉的方法,添加了一个&符号,如下所示:

for((i=114;i<=255;i+=1)); do echo $i > numbers.txt; python DoMyScript.py &; done
Run Code Online (Sandbox Code Playgroud)

但是,这会导致以下错误:

-bash: syntax error near unexpected token `;'
Run Code Online (Sandbox Code Playgroud)

iru*_*var 114

删除;之后&。这是句法要求

for((i=114;i<=255;i+=1)); do echo $i > numbers.txt;python DoMyScript.py & done
Run Code Online (Sandbox Code Playgroud)

  • 虽然这回答了问题,但这可能不是 OP 想要的,因为当所有 python 实例启动和初始化时,它们都会看到一个包含“255”的“numbers.txt”。 (6认同)
  • 该死,43 秒太慢了 :) (3认同)
  • 这是 bash 手册参考:http://www.gnu.org/software/bash/manual/bashref.html#Lists (3认同)
  • @MartinvonWittich,以同情的方式对您的回答+1 ;-) (2认同)
  • 同意@StephaneChazelas,但我不确定我们能用问题中给出的信息真正做些什么。对此的适当解决方案可能需要更多上下文。 (2认同)

gle*_*man 16

鉴于 Stephane 对1_CR's answer的评论,您可能想要:

for i in {114..255}; do { echo $i > numbers.txt && python DoMyScript.py; } & done
Run Code Online (Sandbox Code Playgroud)

  • (续)……但是,除此之外,我看不出这有什么真正的改进。您仍然有 112 个 ``python DoMyScript.py`` 进程以毫秒为间隔生成。除非 `DoMyScript.py` 启动*非常*快,在 `DoMyScript.py` 读取它应该读取的值之前,shell 循环将继续并将下一个值写入 `numbers.txt`。 (2认同)

Mar*_*ich 14

失去;

for((i=114;i<=255;i+=1)); do echo $i > numbers.txt;python DoMyScript.py & done
Run Code Online (Sandbox Code Playgroud)


G-M*_*ca' 6

正如其他答案所述:

  • 问题是;&.
  • 删除;之后的&将导致运行时不会出现 shell 语法错误的命令。但是,它不太可能正常运行,因为它会产生竞争条件。

OP 表示他通过增加 3 秒延迟解决了竞争条件。

没有人提到:

  • 今天可能会有 3 秒的延迟。明天,计算机可能会更加迟钝,并且可能再次出现故障。明年,脚本可能会被修改为更耗时,并且可能再次失败。如果将延迟增加一个数量级,我的意思是 30 秒,则失败的可能性会降低。当然,这意味着脚本进程将(可能?/?大概)按顺序运行,而不是并发(并行)运行,从而破坏了使进程异步的目的。
  • 如果需要运行脚本的多个实例,最好修改脚本以接受命令行上的 number 参数:
    for ((i=114;i<=255;i++)); do python DoMyScript.py --number="$i" & done
    
    Run Code Online (Sandbox Code Playgroud) 并取消该文件。
  • 如果无法更改脚本,则此解决方案应该有效:
    for i in {114..125}; do ( subdir="dir.$i" && mkdir "$subdir" && cd "$subdir" && 
              echo "$i" > numbers.txt && python ../DoMyScript.py; cd .. && rm -r "$subdir" ) & done
    
    Run Code Online (Sandbox Code Playgroud) 这为每个脚本进程提供了一个单独的numbers.txt文件,通过为其提供一个单独的目录来运行。在子shell 中创建和删除目录,并运行脚本。
    • 如果DoMyScript.py访问除numbers.txt 相对路径名以外的任何文件,则需要调整上述命令以适应这种情况。

    • 以上是受到格伦杰克曼的回答的启发 (尽管我相信我的会起作用而他的不会)。

    • 如果命令B取决于命令的成功A,那么防御性编程比. 但这可能不是处理这种情况的理想方式。如果 失败,那么接下来的 111 次尝试也可能会失败,您将收到 112 条错误消息。如果发生致命错误,最好中止循环。A && BABmkdir dir.114

      动作发生在异步子 shell 中这一事实使得这有点棘手。

      for i in {114..125}; do
              { subdir="dir.$i" && mkdir "$subdir" && echo "$i" > "$subdir"/numbers.txt; } || break;
              ( cd "$subdir" && python ../DoMyScript.py; cd .. && rm -r "$subdir" ) & done
      
      Run Code Online (Sandbox Code Playgroud)

      如果 amkdir 或 命令失败,将导致循环中止 。echo value > file

    • 在“保证”可写的目录中工作可能会更好,例如 /tmp. 但是,这会增加您的命令与其他某些进程发生冲突(干扰)的风险。

      • 您可以通过添加$$到目录名称来缓解这种情况;例如, dir.$$.$i甚至 dir.$BASHPID
      • 这不会消除mkdir 由于文件系统已满而导致文件创建或文件创建失败的风险 。
    • 请注意,即使脚本失败,上面的代码也会继续运行并删除临时目录。在这种情况下,您可能想做其他事情。

      • 哎呀。如果由于某种原因,mkdir "$subdir"成功但cd "$subdir"失败了,这将继续执行cd .. && rm -r "$subdir"。如果dir.114在您的父目录中存在名称类似的 目录(即与您的当前目录平行),它将被删除。我相信您可以(至少在某种程度上)通过将最后一行更改为
        ( cd "$subdir" && { python ../DoMyScript.py; cd ..; } && rm -r "$subdir" ) & done
        
        Run Code Online (Sandbox Code Playgroud) 或使用绝对路径;例如,subdir="$PWD/dir.$i"