有没有什么办法可以让我把长时间运行的任务排成队列?

有没有什么办法可以让我把长时间运行的任务排成队列?

有没有办法可以在 Unix 终端中执行以下操作:

  1. 启动长时间运行的进程
  2. 添加另一个长时间运行的进程,在前一个进程完成后启动
  3. 重复步骤 2,直到我将需要运行的进程排入队列

我问这个问题的原因是我需要做一些长时间运行的事情。我可以将所有命令放在一个简单的 bash 脚本中并执行它,但问题是我并不总是确定我需要运行什么。所以如果我启动脚本然后想起另一个我应该运行的脚本,我需要多次按下 ^C 直到所有进程都被终止,编辑脚本并添加我的新进程,然后重新启动整个过程。

我现在具体要做的事情是将大量大文件复制到各种外部硬盘上,由于我不知道具体需要复制哪些文件以及复制到哪里,所以我想先从我知道需要复制的文件开始,然后在弄清楚其余文件后再将它们添加到队列中。

希望这是有意义的...这样的事情可能发生吗?

答案1

这个外壳非常适合这个用途。

  1. 启动第一个程序。
  2. 按下Ctrl-z可暂停程序。
  3. 开始下一个程序。
  4. 重复步骤 2 和 3,将更多程序添加到队列中。所有程序都将处于暂停模式。
  5. 现在像这样运行队列:

    while fg; do :; done
    

    在队列完成之前,您可能无法暂停此 while 循环或退出 bash。

如果您需要注销(例如程序将运行很多天),那么请考虑运行上述步骤screen

答案2

不是很优雅,但是又快又脏:

process1 &
while kill -0 $! ; do sleep 1; done && process2 &
while kill -0 $! ; do sleep 1; done && process3 &

$!如果您运行了中间的后台作业,则需要替换实际的 PID 。

答案3

对你这个一般性问题的回答是:当然。在计算的早期,批处理是做任何事情的唯一方法,即使在发明了多用户交互系统之后,批处理能力也是大型作业的常态。如今,在中型和大型环境中,使用以下系统仍然很常见:太阳网格引擎或者扭矩

但是,这可能超出了您的需要。您可以设置一个更轻量级的系统来在串行队列中运行脚本,但我认为这种方法并不特别适合您的特定任务。假设并行复制到不同的驱动器是可以接受的,我想我会像这样解决它:

  1. 创建与您的目标驱动器相对应的目录结构:

    ~/copysystem/drive1/ ~/copysystem/drive2/ ~/copysystem/drive3/

  2. 安装英科龙

  3. 为每个目录设置一个 incrontab 条目,当出现 IN_MOVED_TO 时,该条目会自动运行复制脚本。

  4. 制作脚本任何一个a) 在启动时终止同一脚本的所有先前实例或 b) 使用mkdir基于的锁文件并阻止,直到锁被清除。

然后,您需要做的就是将文件移动到各个~/copysystem/drive#目录,它们都会神奇地复制到您的目的地。

尤其是在 4a 的情况下,您可能希望使用 来rsync -aP复制文件,以便可以从中间重新开始部分传输。(--remove-sent-files如果您想删除原始文件,可以与 结合使用。)

如果您想跳过使用 incron 的复杂性,您仍然可以利用让脚本在锁定文件上阻塞的优势。 它的工作原理如下:

#!/bin/bash
LOCKDIR="/var/run/copysystem/copysystem.lock"

while ! mkdir $LOCKDIR ; do
  echo "waiting for lock"
  sleep 5
done
trap rmdir $LOCKDIR EXIT

rsync commands go here....

这样做是因为mkdir它是一项原子操作 — 如果成功,您就知道目录不存在。这很重要,因为如果您使用类似的东西! -f && touch,就会出现竞争条件。(与扫描进程表中的 rsync 命令或类似命令相同。)

答案4

您需要的是非交互式命令队列。好消息!我为您写了一个。

enqueue_cmd

#!/usr/bin/perl -w

# Enqueue a job

use strict;

use Fcntl qw(:flock);

my $JOB_QUEUE_FILE = '/var/tmp/job_queue';
my $LOCK_TRIES = 5;
my $LOCK_SLEEP = 1;

my $jq;
open $jq, ">> $JOB_QUEUE_FILE" or die "!open $JOB_QUEUE_FILE: $!";

my $locked = 0;

LOCK_ATTEMPT: for (my $lock_tries = 0; $lock_tries < $LOCK_TRIES;
    $lock_tries++)
{
    if (flock $jq, LOCK_EX) {
            $locked = 1;
            last LOCK_ATTEMPT;
    }

    sleep $LOCK_SLEEP;
}

$locked or die "could not lock $JOB_QUEUE_FILE";

for (@ARGV) {
    print $jq "$_\n";
}

close $jq;

dequeue_cmd

#!/usr/bin/perl -w

# Dequeue a jobs and run them

use strict;

use Fcntl qw(:seek :flock);
use FileHandle;

my $QUEUE_FILE = '/var/tmp/job_queue';
my $OUTPUT_FILE = '/var/tmp/job_out';
my $LOCK_TRIES = 5;
my $LOCK_SLEEP = 1;
my $JOB_SLEEP = 1;

my $locked;

my $jo;
open $jo, ">> $OUTPUT_FILE" or die "!open $OUTPUT_FILE: $!";
$jo->autoflush(1);

my $jq;
open $jq, "+< $QUEUE_FILE" or die "!open $QUEUE_FILE: $!";

my @jobs = ( );
my $job;

JOB: while (1) {

    if (-s $QUEUE_FILE == 0) {
            sleep $JOB_SLEEP;
            next JOB;
    }

    $locked = 0    

    LOCK_ATTEMPT: for (my $lock_tries = 0; $lock_tries < $LOCK_TRIES;
            $lock_tries++)
    {
            if (flock $jq, LOCK_EX) {
                    $locked = 1;
                    last LOCK_ATTEMPT;
            }

            sleep $LOCK_SLEEP;
    }
    $locked or die "could not lock $QUEUE_FILE";

    seek $jq, 0, SEEK_SET or die "could not seek to start of file";

    push @jobs, <$jq>;

    truncate $jq, 0 or die "could not truncate $QUEUE_FILE";
    seek $jq, 0, SEEK_SET or die "could not seek to start of $QUEUE_FILE";

    flock $jq, LOCK_UN;

    for $job (@jobs) {
            chomp $job;
            print $jo "## executing $job\n";
            print $jo `$job`;
    }

    sleep $JOB_SLEEP;
}

首先,运行nohup ./dequeue_cmd &,然后添加命令,如下所示:

./enqueue_cmd "echo 'hello world'" "sleep 5" "date"
./enqueue_cmd "ls /var/tmp"

输出出现在/var/tmp/job_out

tail -F /var/tmp/job_out
## executing echo 'hello world'
hello world
## executing sleep 5
## executing date
Fri Dec 10 16:35:43 PST 2010
## executing ls /var/tmp
ff
job_out
job_queue
ss

相关内容