47

我正在尝试使用 bash 在后台并行运行一些命令。这是我正在尝试做的事情:

forloop {
  //this part is actually written in perl
  //call command sequence
  print `touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;`;
}

反引号 (``) 之间的部分生成一个新的 shell 并连续执行命令。问题是,只有在执行完最后一个命令后,才会返回对原始程序的控制。我想在后台执行整个语句(我不期待任何输出/返回值)并且我希望循环继续运行。

调用程序(具有循环的程序)在所有生成的 shell 完成之前不会结束。

我可以在 perl 中使用线程来生成调用不同 shell 的不同线程,但这似乎有点矫枉过正......

我可以启动一个 shell,给它一组命令并告诉它进入后台吗?

4

15 回答 15

32

我还没有测试过这个但是怎么样

print `(touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;) &`;

括号意味着在子shell中执行,但这不应该受到伤害。

于 2008-10-02T08:24:17.067 回答
21

谢谢休,做到了:

adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped")
started
stopped
adrianp@frost:~$ (echo "started"; sleep 15; echo "stopped") &
started
[1] 7101
adrianp@frost:~$ stopped

[1]+  Done                    ( echo "started"; sleep 15; echo "stopped" )
adrianp@frost:~$ 

其他想法不起作用,因为它们在后台启动每个命令,而不是命令序列(这对我来说很重要!)。

再次感谢你!

于 2008-10-02T08:42:23.560 回答
21

另一种方法是使用以下语法:

{ command1; command2; command3; } &
wait

请注意,&在命令组的末尾,而不是在每个命令之后。最后一个命令之后的分号是必要的,第一个括号之后和最后一个括号之前的空格也是如此。at the waitend 确保在生成的子进程(命令组)结束之前父进程不会被杀死。

你也可以做一些花哨的事情,比如重定向stderrstdout

{ command1; command2; command3; } 2>&2 1>&1 &

您的示例如下所示:

forloop() {
    { touch .file1.lock; cp bigfile1 /destination; rm .file1.lock; } &
}
# ... do some other concurrent stuff
wait # wait for childs to end
于 2013-09-26T13:15:45.817 回答
14
for command in $commands
do
    "$command" &
done
wait

命令末尾的 & 符号在后台运行它,并wait等待后台任务完成。

于 2008-10-02T08:02:41.063 回答
5

GavinCattell最接近(对于 bash,IMO),但正如 Mad_Ady 指出的那样,它不会处理“锁定”文件。这应该:

如果还有其他待处理的作业,则等待也会等待这些作业。如果您只需要等待副本,则可以累积这些 PID 并仅等待那些。如果没有,您可以使用“pids”删除 3 行,但它更通用。

此外,我添加了检查以完全避免复制:

pids=
for file in bigfile*
do
    # Skip if file is not newer...
    targ=/destination/$(basename "${file}")
    [ "$targ" -nt "$file" ] && continue

    # Use a lock file:  ".fileN.lock" for each "bigfileN"
    lock=".${file##*/big}.lock"
    ( touch $lock; cp "$file" "$targ"; rm $lock ) &
    pids="$pids $!"
done
wait $pids

顺便说一句,您似乎正在将新文件复制到 FTP 存储库(或类似存储库)。如果是这样,您可以考虑使用复制/重命名策略而不是锁定文件(但这是另一个主题)。

于 2009-11-05T20:11:52.683 回答
3

您正在寻找的 bash 工具称为Compound Commands. 有关更多信息,请参见手册页:

复合命令 复合命令是以下之一:

   (list) list  is  executed  in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below).  Variable assignments and
          builtin commands that affect the shell's environment do not remain in effect after  the  command  completes.   The
          return status is the exit status of list.

   { list; }
          list  is  simply  executed in the current shell environment.  list must be terminated with a newline or semicolon.
          This is known as a group command.  The return status is the exit status of list.  Note that unlike the metacharac‐
          ters  (  and  ),  {  and  } are reserved words and must occur where a reserved word is permitted to be recognized.
          Since they do not cause a word break, they must be separated from list by whitespace or another shell  metacharac‐
          ter.

还有其他的,但这些可能是最常见的两种类型。第一个,括号,将在子shell中运行一系列命令,而第二个,花括号,将在当前shell中连续运行命令列表。

括号

% ( date; sleep 5; date; )
Sat Jan 26 06:52:46 EST 2013
Sat Jan 26 06:52:51 EST 2013

大括号

% { date; sleep 5; date; }
Sat Jan 26 06:52:13 EST 2013
Sat Jan 26 06:52:18 EST 2013
于 2013-01-26T12:05:49.843 回答
2

使用 at 作业运行命令:

# date
# jue sep 13 12:43:21 CEST 2012
# at 12:45
warning: commands will be executed using /bin/sh
at> command1
at> command2
at> ...
at> CTRL-d
at> <EOT>
job 20 at Thu Sep 13 12:45:00 2012

结果将通过邮件发送到您的帐户。

于 2012-09-13T10:52:40.503 回答
2

我在这里偶然发现了这个线程,并决定整理一个代码片段来生成链式语句作为后台作业。我在 BASH for Linux、KSH for IBM AIX 和 Busybox 的 ASH for Android 上对此进行了测试,所以我认为可以肯定地说它适用于任何Bourne-like shell。

processes=0;
for X in `seq 0 10`; do
   let processes+=1;
   { { echo Job $processes; sleep 3; echo End of job $processes; } & };
   if [[ $processes -eq 5 ]]; then
      wait;
      processes=0;
   fi;
done;

此代码运行多个后台作业,达到一定的并发作业限制。例如,您可以使用它来重新压缩大量 gzip 文件,xz而不会有大量xz进程占用您的整个内存并使您的计算机崩溃:在这种情况下,您可以使用*' fors list 和批处理作业是gzip -cd "$X" | xz -9c > "${X%.gz}.xz"

于 2014-04-08T21:48:18.920 回答
2

在子shell中运行命令:

(command1 ; command2 ; command3) &
于 2016-11-19T10:18:19.670 回答
1

尝试用 &s 将命令放在大括号中,如下所示:

{command1 & ; command2 & ; command3 & ; }

这不会创建子 shell,而是在后台执行该组命令。

高温高压

于 2008-10-02T08:05:24.177 回答
1

我不知道为什么没有人回答正确的解决方案:

my @children;
for (...) {
    ...
    my $child = fork;
    exec "touch .file1.lock; cp bigfile1 /destination; rm .file1.lock;" if $child == 0;
    push @children, $child;
}
# and if you want to wait for them to finish,
waitpid($_) for @children;

这会导致 Perl 生成子进程来运行每个命令,并允许您等待所有子进程完成后再继续。

顺便一提,

print `some command`

system "some command"

将相同的内容输出到标准输出,但第一个具有更高的开销,因为 Perl 必须捕获所有“ some command”的输出

于 2008-10-03T21:45:55.243 回答
1

在 for 循环中分叉:

for i in x; do ((a; b; c;)&); done

例子:

for i in 500 300 100; do ((printf "Start $i: "; date; dd if=/dev/zero of=testfile_$i bs=1m count=$i 2>/dev/null; printf "End $i: "; date;)&) && sleep 1; done

于 2014-11-01T02:53:59.517 回答
0

万一有人仍然感兴趣,您可以在不调用这样的子shell的情况下执行此操作:

print `touch .file1.lock && cp bigfile1 /destination && rm .file1.lock &`;
于 2014-10-27T13:44:43.623 回答
0

您可以使用 GNUparallel命令并行运行作业。它更安全更快。

我的猜测是您正在尝试将多个大文件从源复制到目标。为此,您可以与以下语句并行执行此操作。

$ ls *|parallel -kj0 --eta 'cp {} /tmp/destination'

由于我们使用-j0了选项,所有文件将被并行复制。如果您需要减少并行进程的数量,那么您可以使用-j<n>where<n>是要执行的并行进程的数量。

Parallel 还会收集进程的输出,并以-k其他作业控制机制无法做到的顺序方式(带有选项)进行报告。

--eta选项将为您提供正在进行的过程的详细统计信息。所以我们可以知道这个过程有多少完成了,需要多长时间才能完成。

于 2014-11-01T07:23:02.933 回答
0

您可以将参数传递给命令组(具有顺序命令)并在后台运行它们。

for hrNum in {00..11};
do
    oneHour=$((10#$hrNum + 0))
    secondHour=$((10#$hrNum + 12))
    { echo "$oneHour"; echo "$secondHour"; } &
    wait
done
于 2019-07-10T05:31:02.353 回答