如何像在队列中一样运行命令


42

我需要将各种文件复制到各种文件夹中。我可以将所有复制命令添加到bash脚本中,然后运行该脚本,但是如果要向该复制“队列”添加更多命令,则必须等到它完成。

有什么方法可以将命令作为队列运行,以及在事物运行时向该队列添加更多命令?

用不同的方式解释,我想开始一个长期运行的任务。在运行时,我要启动另一个直到第一个完成后才真正启动的启动。然后在最后一个之后添加另一个,依此类推。这有可能吗?


3
我可以建议您接受可以解决您问题的答案。似乎有一些对您有用。
holmb

2
是的,您可能会的,而我一直想。问题是我在一家使用Mac的公司工作时问了这个问题。我不再在那里工作,我自己也没有Mac,因此无法测试其中任何一个。当我无法测试一个答案是否有效时,不要对将其标记为答案感到不自在,所以现在我希望人们投票赞成他们认为对他们而言效果很好的答案,以便以这种方式出现“答案”代替。
Svish

Answers:


25

at实用程序以在指定时间运行命令而闻名,它还具有队列功能,可以要求立即开始运行命令。它从标准输入读取要运行的命令。

echo 'command1 --option arg1 arg2' | at -q myqueue now
echo 'command2 ...' | at -q myqueue now

batch命令等价于at -q b -m now-m意味着命令输出,如果有的话,将像cron一样邮寄给您)。并非所有的Unix变体都支持队列名称(-q myqueue);您可能只限于一个名为的队列b。Linux at仅限于单字母队列名称。


2
至少在Linux上,这似乎并不等待上一个命令完成。
wds 2015年

@wds在Debian Wheezy上为我工作。它在哪里以及如何为您打破?请注意,仅等待命令完成;如果命令启动的子进程的父进程超过其父进程,则at不会等待子进程。
吉尔(Gilles)'所以

我在CentOS 6上进行了尝试。我仅用一个简单的“ sleep x”进行了测试。我不确定运行at会做什么,但是我假设它启动了一个子shell,并且该子shell应该等待结果(睡眠调用不会立即返回)。
wds 2015年

除了队列名称之外,“ now”也不是标准名称:Ubuntu上的bash正在抱怨它……
Winwaed

@winwaed哦,是now,不是-t now,抱歉。我没有注意到示例代码中的错误。
吉尔(Gilles)'所以

23

附加&到命令的末尾以将其发送到后台,然后wait在运行下一个命令之前将其发送到后台。例如:

$ command1 &
$ wait; command2 &
$ wait; command3 &
$ ...

2
好极了!当您开始执行某些操作,然后查看stackoverflow以寻找在其后排队的方法时,此语法非常棒
Erik Aronesty

2
如果您同时改变主意,可以怎么做来中止wait?我所有的杀手似乎都无法渗透。
肯·威廉姆斯

1
您只需终止命令即可。该wait直到以前的完成只是停止作业。
GertSønderby'15

可以使用nohup吗?
迈克尔

11

布拉德和曼科夫的解决方案都是不错的建议。类似于两者结合的另一个方法是使用GNU Screen来实现您的队列。这样做的优点是能够在后台运行,您可以随时检查它,排队新命令只是将它们粘贴到缓冲区中,以便在先前命令退出后执行。

首轮:

$ screen -d -m -S queue

(顺便说一下,现在是玩一些很棒的.screenrc文件的好时机)

这将为您命名的队列产生一个后台屏幕会话。

现在,根据需要排队尽可能多的命令:

screen -S queue -X stuff "echo first; sleep 4; echo second^M"

我在上面做多个命令只是为了测试。您的用例可能更像:

screen -S queue -X stuff "echo first^M"
screen -S queue -X stuff "echo second^M"

请注意,上面我的行中的“ ^ M”是一种获取嵌入式换行符的方法,稍后将其填充到现有bash shell中后将对其进行解释。使用“ CTL-V”获得该序列。

制作一些简单的shell脚本以使其自动化并使命令排队很容易。然后,每当您要检查后台队列的状态时,都可以通过以下方式重新连接:

screen -S queue -r

从技术上讲,您甚至不需要命名屏幕会话,它就可以正常工作,但是一旦您迷上了它,您将想要一直保持运行状态。;-)

当然,如果这样做,另一种好方法是将当前窗口之一命名为“ queue”并使用:

screen -S queue -p queue -X stuff "command"

看起来这基本上只是在运行屏幕会话中“键入”命令,以便当外壳程序在第一个命令完成后再次获得控制权时,该命令将启动。换句话说,它等同于@mankoff的解决方案,但是使用了更高级的工具来进行键入。
肯·威廉姆斯

几乎一样-主要区别在于屏幕解决方案支持更好的自动化。您可以手工完成一些事情,使用脚本完成其他事情,所有这些都需要一个简单的界面。
乔丹

@Jordan很好,它对我有用。但是-X之后的“东西”是什么?
康斯坦丁

@Konstantin gnu.org/software/screen/manual/html_node/Paste.html#Paste(TL ; DR-终端输入的内容随便输入)
约旦

@乔丹哦,我明白了。这是一个命令。我认为这是一个任意字符串。
康斯坦丁

8

对于您所描述的用例,我已经成功使用了一个实用程序。最近,我将我的主要笔记本电脑移至了新硬件,这涉及到将某些文件移至NAS,并将其余文件移至新计算机。

这就是我做的。

  1. 设置与网络连接有关的所有计算机,以便它们可以相互访问。

  2. 在要从中移动文件的计算机(以下称为源计算机)上,安装rsync和apt-get install rsync和秘密保护的任务后台处理程序(网站http://vicerveza.homeunix.net/~viric/soft/ts/)。如果您在Debian上,则软件包名称为tsis,task-spooler并且将可执行文件重命名,tsp以避免名称与软件包中的ts可执行文件冲突moreutils。通过网站链接的Debian软件包可以完美地安装在Ubuntu上dpkg -i task-spooler_0.7.3-1_amd64.deb或类似版本上。

  3. 还要确保所有计算机都已通过SSH安装了SSH apt-get install openssh-server。在源计算机上,您需要设置SSH以允许无密码登录到目标计算机。大多数将使用的方法是使用的公钥身份验证ssh-agent(有关示例,请参见https://www.google.se/search?q=ssh+public+key+authentication),但是我有时会使用一种更简单的方法以及密码验证。将以下内容添加到SSH客户端配置(~/.ssh/config/etc/ssh/ssh_config):

    Host *
         ControlMaster auto
         ControlPath ~/.ssh/master-%r@%h:%p
    

    然后在源计算机上打开一个终端,使用进行登录ssh target1,照常进行身份验证,然后将该终端保持打开状态。请注意,有一个名为的套接字文件~/.ssh/master-user@target1:22,该文件将保持已认证的主会话打开,并允许后续的无密码连接user(只要连接使用相同的目标主机名和端口)。

    此时,您需要验证您是否可以登录而不提示您对目标计算机进行身份验证。

  4. 现在运行ts rsync -ave ssh bigfile user@target1:单个文件或ts rsync -ave ssh bigdir user@target1:目录。使用rsync时,不要在目录中包含斜杠(bigdir与相比bigdir/),这一点很重要,否则rsync会假定您的意思与bigdir/*大多数其他工具相同。

    任务后台处理程序将返回提示,并让您连续将许多这些命令排队。检查ts不带参数的运行队列。

Task Spooler具有许多功能,例如重新安排运行队列,仅在另一个作业成功运行时才运行特定作业等ts -h。有时我会在运行时检查命令输出ts -c

还有其他方法可以执行此操作,但是对于您的用例,它们都包括Task Spooler。我选择在SSH上使用rsync来保留文件时间戳,而通过SMB复制则不会。


谢谢您的回答,我不知道ts,它看起来非常强大且易于使用,只是在github上自动分叉了
亚历克斯

@Alex我看了看你的fork,尤其对在自动工具化和原始化debian方面所做的事情特别感兴趣,并查看了你的提交,这与原始来源的区别并不容易。您介意强行推送新的提交吗?在步骤1中,该提交将导入原始源,而在提交的内容中添加2.提交?它将帮助某人浏览您的源,以(更容易)信任您对原始源的添加。这是我没有从PPA或类似产品中安装的少数软件包之一,因此最好自己用叉子来构建。
holmb 2014年

您知道我必须更换眼镜,我对您现有的task-spooler:-) 回答的部分内容丢失了。无论如何,这是您的意见中的一个很好的建议,我会尽快做的。
亚历克斯

使用渐进式提交完成,删除和重新创建存储库,只有几个错误(在推送之前应代谢更多本地提交)
Alex

4

我也经常需要这样的东西。我编写了一个小实用程序after,每当其他进程完成时,该实用程序就会执行命令。看起来像这样:

#!/usr/bin/perl

my $pid = shift;
die "Usage: $0 <pid> <command...>" unless $pid =~ /^\d+$/ && @ARGV;

print STDERR "Queueing process $$ after process $pid\n";
sleep 1 while -e "/proc/$pid";
exec @ARGV;

然后,您可以像这样运行它:

% command1 arg1 arg2 ...  # creates pid=2853
% after 2853 command2 arg1 arg2 ... # creates pid=9564
% after 9564 command3 arg1 arg2 ...

与其他方法相比,此方法的最大优点是不需要以任何特殊方式运行第一个作业,您可以在新作业中遵循任何流程。


不错的脚本@ken。尽管我建议您尝试Task Spooler,因为您似乎有时会需要这样做。看我的答案。
holmb

看起来很整洁,谢谢。TS缺少的一件事似乎是能够跟踪不是在TS下开始的工作的能力。尽管我猜您可以启动一个新的TS作业,其目的只是为了等待现有过程完成。
肯·威廉姆斯

确实。这是解决方案的一个有用方面。
holmb

2

Command1 && Command2

  • “ &&”表示command2仅在command1完成且返回码为0后才运行
  • 注意:|| 表示command2仅在command1完成后返回0以外的代码后才运行

1

您可以简单地将命令添加到已经在运行命令的Shell的命令行中。

仔细键入或在其他位置键入,验证并剪切并粘贴。即使当前命令正在输出行,您仍然可以键入新内容,然后按Enter键,并且在所有命令运行或输入完毕之前它将运行。

示例如下。您可以剪切并粘贴整个内容,也可以在第一个运行时(如果您输入的速度非常快)或在第二个运行时输入以下命令:

echo "foo"
sleep 10; echo "bar"
sleep 3
echo "baz"

0

我能想到的最骇人听闻的方法是使用/ tmp中的简单锁定文件来维护队列“状态”。当file1.sh执行cp命令时,它将在/ tmp中保留一个锁定文件(或硬链接)。完成后,删除文件。其他所有脚本都必须在/ tmp中查找具有您选择的命名方案的锁定文件。当然,这会遭受各种失败的影响,但是设置起来很琐碎,并且完全可以在bash中实现。


难以在实践中使用,因为它需要你想运行需要每一项工作进行修改,以保持锁定文件,并需要知道(或接受作为参数),其文件锁定使用。只要有可能,最好在工作外部进行排队。
肯·威廉姆斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.