从现在开始的某个时候做一些事情(也许还会在控制台中显示结果)


12

我使用Ubuntu服务器16.04,并且希望at在当前会话中使用该实用程序从现在开始1分钟(例如echo)进行操作,而没有给出具体的日期和时间-比当前时间提前1分钟。

这失败了:

echo 'hi' | at 1m

我之所以选择atsleep是因为睡眠妨碍了当前会话,因此更适合于延迟另一会话中的命令,而不是我们大多数时间都在使用的命令。航空(AFAIR),at这种举止不会妨碍我的会议。

更新_1

通过Pied Piper的回答,我已经尝试过:

(sleep 1m; echo 'hi')  &

我对此方法有疑问:“ hi”流打印在我的主要提示内,并且还在_包含它的主要提示下添加了一个空的辅助提示(),请参见:

USER@:~# (sleep 1m; echo 'hi')  &
[1] 22731
USER@:~# hi
^C
[1]+  Done

更新_2

通过彼得·科德的回答,我尝试过:

(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)

看来,这在Bash 4.4中正常运行,但在某些旧版本中似乎无效(请参见答案中的注释)。我个人在自己的环境中使用Bash 4.3。


2
我希望kill标志是GNU风格的并且可以重新排列,这样听起来就不会像“ kill the wench”!
NH。

Answers:


6

“ hi”流将打印在提示中(在中stdin),而不是在我的提示中stdout

不,它不是 “印在您的文件中stdin”。

如果按回车键,它将不会尝试hi作为命令运行。只是当光标在提示后时才echo打印背景hi


也许您想打印一个领先的换行符,例如(sleep 1m && echo -e '\nhi' &)。(根据需要echo在您的shell中进行调整。或printf用于可移植性。)

我将&子外壳放在内部以“取消”后台作业,因此您也避免了的“干扰” [1]+ Done。随着&&命令之间,&适用于整个复合命令链,因此它返回到shell提示符马上,而不是等待sleep退出就像你用得到(sleep 1; echo ... &)

发生的情况(延迟1秒):

peter@volta:~$ (sleep 1 && echo -e '\nhi' &)
peter@volta:~$ 
hi
       # cursor is at the start of this empty line.

Bash不知道该echo打印了什么,因此它不知道需要重新打印其提示或清理屏幕。您可以使用手动进行操作control-L

您可以编写一个shell函数,该函数使bash在echo完成后重新打印其提示。只做echo "$PS1"不会复制任何已经键入的字符。 kill -WINCH $$在我的系统上,bash会在不清除屏幕的情况下重新打印其提示hi行,而是在提示之前留一行。当WINdow大小更改时,SIGWINCH会自动发送,而bash对它的响应恰好达到了我们想要的目的。

它可能与其他shell一起使用,但是我并没有尝试使这种100%可移植性/ POSIX。(不幸的是,这仅适用于bash4.4,而不适用于bash4.3,或取决于我不知道的某些设置

  # bash4.4
peter@volta:~$ (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
peter@volta:~$ ljlksjksajflasdf dfas      # typed in 2 seconds
hi
peter@volta:~$ ljlksjksajflasdf dfas      # reprinted by Bash because of `kill -WINCH`

您可以轻松地将其包装在一个带有sleep arg和一条消息的shell函数中。

bash 4.3不会在SIGWINCH之后重新打印其提示,因此在此不起作用。我已经shopt -s checkwinsize在两个系统上都启用了,但是它仅在Bash 4.4系统(Arch Linux)上有效。

如果不起作用,则可以尝试使用(sleep 2 && echo -e '\nhi' && echo "$PS1" &),如果命令行为空,则可以“起作用”。(它也忽略了PROMPT_COMMAND设置的可能性。)


没有真正干净的方法来使终端输出与稍后触发计时器时可能在终端上执行的操作混合。如果您正在向中滚动某些内容less,则很容易会错过它。

如果您正在寻找具有交互结果的内容,建议您播放音频文件。例如,使用命令行播放器mpv(如MPlayer2的漂亮叉子)。或选择一个视频文件,以便打开一个新窗口。

(sleep 1 && mpv /f/share/music/.../foo.ogg </dev/null &>/dev/null &)

您可能需要echo打开一个新的xterm / konsole / gnome-terminal。使用一个选项,在命令退出后使终端保持打开状态。


大卫,非常感谢您,我已经提出建议。这实际上是我所寻找的最接近的东西。有没有一种方法可以升级以下命令,以便Enter在回显出现后立即模拟按键事件,这样我将自动回到控制台的主提示符下(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
Arcticooling

@Arcticooling:kill -WINCH $$ 确实刷新了在终端中运行的shell的提示。那就是使用它的全部重点。按下Enter键将提交部分键入的命令,但是WINCH不会,如我所展示的。如果您没有输入任何内容,则会显示一个空提示。
彼得·科德斯

1
如果开始rm -rf ~/some/directory输入,可能在错误的时间输入Enter rm -rf ~/。(安全提示:完成键入路径,然后按 Control-a并更改lsrm -rf,这样随时使用Enter不会发胖。)
Peter Cordes

连线,我执行了,(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)并且在出现回声之后,我得到了一个空提示,只有在击中后才Enter返回到主提示...很抱歉,如果我误解了您,我对Bash还是陌生的。
Arcticooling

@Arcticooling:我在Konsole终端模拟器中的Arch Linux上使用Bash 4.4。我希望我的命令至少可以移植到其他版本的bash,但可能不是:(是的,刚刚在screen较旧的Ubuntu系统上的会话中(并且仅通过SSH)在Bash 4.3中进行了测试,并且我得到了您描述的行为。
彼得·科德斯

13

正确的用法是at <timespec>,其中<timespec>是时间规范。您还可以使用该-f选项来指定包含要执行的命令的文件。如果-f不使用重定向,则at将读取从stdin(标准输入)执行的命令。

在您的示例中,要在按下Enter(现在)一分钟后立即执行“某些命令”(在下面的示例中,我将“某些命令”选择为“ echo hi”),所以<timespec>使用的是now + 1 minute。因此,为了通过单层解决方案获得所需的结果,请使用以下命令:

echo 'echo hi' | at now + 1 minute

echo hi一分钟后(应该这样做)可以运行命令。这里的问题是该echo hi命令将由伪tty中的at命令执行,并且输出将被发送到用户的邮箱(如果配置正确-这需要对如何在Windows中配置邮箱进行更详细的说明) UNIX机器,并且不属于此问题的范围)。最重要的是,您将无法echo hi在发出命令的同一终端中直接看到的结果。最简单的选择是将其重定向到“某些文件”,例如:

echo 'echo hi > /tmp/at.out' | at now + 1 minute

在上面的示例中,“ some file”为/tmp/at.out,预期结果是at.out/ tmp下的文件在一分钟后创建并包含文本“ hi”。

除上述now + 1 minute示例外,还可以使用许多其他时间规范,甚至是一些复杂的时间规范。at命令足够聪明,可以解释易于阅读的代码,例如以下示例:

now + 1 day13:25mid‐nightnoon,...

有关可能的时间规格,请参考at的手册页,因为可能的变化范围很广,可能包括日期,相对或绝对时间等。


2
at默认情况下通过邮件发送输出,但这需要正确配置才能工作。
西蒙·里希特

我现在提供100名代表悬赏奖励。请阅读下面的悬赏消息,并进行编辑以详细说明什么是at <timespec>标志以及您使用的标志以及它是否适合Ubuntu 16.04 xenial。
Arcticooling

好的,我增加了答案的详细程度,以使现在的教学更具说服力。对于某些人来说,这可能听起来有些漫长,但我相信它毫无疑问地留下了疑问,因为它清楚地列举了有效的示例以及它们如何工作。
Marcelo

7

sleep在子shell中运行:

(sleep 60; echo -e '\nhi')  &

注意:在Linux上,您可以指定以秒为单位或以后缀s / m / h / d(秒,分钟,小时,天)为后缀的参数。在BSD上,参数必须以秒为单位


我对此方法有一个小问题---“ hi”流打印在提示中(在中stdin),而不是在我的提示中(stdout与常规一样echo)。
Arcticooling

1
如果您不希望终端中的输出与提示以及其他命令的输出混在一起,则必须将其重定向到某个地方
PiedPiper

此重定向失败@PiedPiper (sleep 10s; echo 'hi') & 1>。我现在阅读更多。
Arcticooling

2
阅读文档始终是一个好主意:)
PiedPiper

1
@Arcticooling似乎您对stdin和的含义感到困惑stdout。它们与终端上出现的内容无关。相反,您应该针对特定过程(基本上是程序)来解释它们。流程的标准输入(“ stdin”)是流程可以从中读取数据的源,流程的标准输出(“ stdout”)是可以向其写入数据的目的地。这些源和目标可以是其他程序或文件,也可以是终端本身(您键入的内容将输入stdin,并且显示与stdout相关的所有内容)。
David Z

3

之所以失败,是因为您正在传递的输出echo "hi",该输出只是hiat,但是at希望它的输入可以由默认系统外壳程序执行(通常是bash,但有时会因系统而有所不同)。

这个:

at 1m << EOF
echo "hi"
EOF

将实现您似乎正在尝试做的事情。它使用称为“ here document”的shell构造,通常是执行此类操作的公认方法(因为它比使用echo命令处理多行代码更好,并且不需要创建新文件,例如,cat),并使用转换为稍微复杂一些的等效项echo

echo 'echo "hi"' | at 1m

可能值得注意的是,at它将把任何命令输出邮寄给调用at命令的用户,而不是像sleep在子shell中延迟使用命令那样将输出传递给终端。特别是,这意味着,除非您对系统进行了一些自定义,或者打算读取本地邮件后台处理程序,否则您将无法获取通过运行的命令的输出at


由于某些原因,您给出的两个示例都给我一个错误syntax error. Last token seen: m Garbled time 。我不知道为什么。我使用Ubuntu 16.04,Bash 4.0。也许您是在另一个系统上完成的。即使我键入时at没有任何其他参数---我仍然会遇到同样的错误。这里有更多数据
Arcticooling

1
at 1m不适用于posix兼容at。该命令应该是at now + 1 minute
PiedPiper

@PiedPiper是正确的,1m与的POSIX兼容版本不兼容at,我只是假设您有一个接受该语法的版本。
奥斯汀·海默加恩

@PiedPiper at 1m不是POSIX,但是的POSIX兼容实现at可以自由地按需对其进行解释,它不会比一个月前返回错误或含义at更不符合解释该含义的实现。IOW,这是不是POSIX 的代码。now + 1 minuteat 1m
斯特凡Chazelas

2

结合@Peter Cordes的答案和这个答案 /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687

下面的代码来自后一个答案,我的改动很小。编译为a.out文件(无论您喜欢什么名称)

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        char *tty = "/dev/tty";
        if (argc > 1)
                tty = argv[1];
        int fd = open(tty, O_WRONLY);

        int i;
        char buf[] = "\n";
        for (i = 0; i < sizeof buf - 1; i++)
                if (ioctl(fd, TIOCSTI, &buf[i]))
                        perror("ioctl");
        close(fd);
        return 0;
}

然后运行下面的命令。(我在目录/ tmp /中放了一个.out,您可能需要更改为您的)

(sleep 2 && echo -e '\nhi' && /tmp/a.out &)

要么

(sleep 2 && echo -e '\nhi' && /tmp/a.out /proc/$$/fd/0 &)

顺便说一句,kill -WINCH $$在Gentoo上使用Bash 4.3对我来说效果很好。


1

根据以上答案,您可以让嵌入式命令将其输出定向到您的终端,如下所示:

echo "echo hi >$(tty); kill -WINCH $$" | at now + 1 minute

tty命令返回当前终端的设备路径,将其包含在其中,$(...)让bash在子外壳中执行该命令,并且kill命令向当前进程发送信号,该信号将使bash重新打印其$ PS1提示符。

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.