在Mac OS X上如何使用超时命令?

149

在Mac OS X上有没有timeout命令的替代方案。基本要求是我能够在指定的时间内运行一个命令。

例如:

timeout 10 ping google.com

这个程序在Linux上运行ping命令10秒钟。


请参见 https://unix.stackexchange.com/questions/43340/how-to-introduce-timeout-for-shell-scripting。 - Joshua Goldberg
6个回答

198

您可以使用

brew install coreutils

每当您需要超时时,请使用

gtimeout

相反,为了解释这一点,这里有来自Homebrew警告部分的片段:

Caveats

All commands have been installed with the prefix 'g'.

If you really need to use these commands with their normal names, you can add a "gnubin" directory to your PATH from your bashrc like:

PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"

Additionally, you can access their man pages with normal names if you add the "gnuman" directory to your MANPATH from your bashrc as well:

MANPATH="/usr/local/opt/coreutils/libexec/gnuman:$MANPATH"

32
然后:别名timeout = gtimeout - nkadwa
3
或者执行 ln -s /usr/local/bin/gtimeout /usr/local/bin/timeout 命令,这样只需要启用一个命令(使用别名的方法适用于交互式CLI使用,而不适用于从bash脚本中调用)。 - John Y
2
在我这边安装了coerutils之后,我得到了一个可用的超时时间:ln -s /usr/local/bin/gtimeout /usr/local/bin/timeout ln: /usr/local/bin/timeout: 文件已存在。 - talsibony
5
更新:我刚刚使用brew install coreutils安装了coreutils,现在timeout命令可以直接使用,不需要前缀了。 - tdensmore
很好 - 但我对引入(然后忘记)依赖关系感到紧张。我更喜欢Perl解决方案,因为它是本地的。 - Mark

36

另一种在几乎所有平台上都有效(因为它使用几乎普及的 Perl 语言)的简单方法如下:

function timeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }

从这里获取: https://gist.github.com/jaytaylor/6527607

你可以不将其放入函数中,而是将以下行放入脚本中,它也可以正常工作:

timeout.sh

perl -e 'alarm shift; exec @ARGV' "$@";

或者是带有内置帮助/示例的版本:

timeout.sh

#!/usr/bin/env bash

function show_help()
{
  IT=$(cat <<EOF

Runs a command, and times out if it doesnt complete in time

Example usage:
 
   # Will fail after 1 second, and shows non zero exit code result
   $ timeout 1 "sleep 2" 2> /dev/null ; echo \$?
   142

   # Will succeed, and return exit code of 0.
   $ timeout 1 sleep 0.5; echo \$?
   0

   $ timeout 1 bash -c 'echo "hi" && sleep 2 && echo "bye"' 2> /dev/null; echo \$?
   hi
   142

   $ timeout 3 bash -c 'echo "hi" && sleep 2 && echo "bye"' 2> /dev/null; echo \$?
   hi
   bye
   0
EOF
)
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi
if [ -z "$1" ]
then
  show_help
fi

#
# Mac OS-X does not come with the delightfully useful `timeout` program.  Thankfully a rough BASH equivalent can be achieved with only 2 perl statements.
#
# Originally found on SO: https://dev59.com/i3RB5IYBdhLWcg3wgXdV
# 
perl -e 'alarm shift; exec @ARGV' "$@";

基本上只需要删除函数并保留Perl行...我在上面提供了更多细节! - Brad Parks
不幸的是,当定时器到期时,这会生成一个“Alarm clock”消息,并且消除它会变得混乱。 - RichVel
1
嘿...你有试过像我展示的例子那样吗?你可以通过这样运行来忽略闹钟消息:./timeout.sh 1 "sleep 2.5" 2> /dev/null; echo $?。请注意,如果您在子shell中运行它,还可以捕获输出,如下所示:RESULT=$(./timeout.sh 1 "sleep 2.5" 2> /dev/null; echo $?) - Brad Parks
1
最好添加ualarm版本,以获得更高的精度:function timeout() { perl -e 'use Time::HiRes "ualarm"; ualarm shift; exec @ARGV' "$@"; }(请注意,根据http://perldoc.perl.org/functions/alarm.html,它需要Perl> = 5.8) - gib
@BradParks 当我使用这个时,会在stderr输出一个“闹钟”消息。一些快速搜索表明这是shell看到SIGALARM的响应,而不是perl实际输出的内容。有什么建议可以抑制它(而不会同时抑制所有使用超时运行的脚本的stderr)? - Jordan
显示剩余2条评论

26

kvz所述,只需使用Homebrew:

brew install coreutils

现在timeout命令已经可以使用了 - 不需要别名(也不需要gtimeout,虽然也可用)。


1
感谢您关于别名不必要的评论。which timeout # /usr/local/bin/timeout 的确如此。 - Joel Mellon

10

您可以使用以下命令限制任何程序的执行时间:

ping -t 10 google.com & sleep 5; kill $!

13
这可能不是你想要的。结果是该命令将会精确地运行5秒钟。也就是说,如果你希望它是最长时间而不是最短时间,那么这不是你所寻找的。 - gesell
一开始我不太明白@gesell在说什么。但是在运行了以下命令之后: ping-t 1 google.com&sleep 5;kill $!我意识到他/她的意思是: 整个表达式将在精确5秒后终止。然而,这是我选择的解决方案,因为我在从Python程序中运行一个bash命令的后台线程——只要它在某个时候终止了,我就很满意了。 - Mark

3

4
更简单的方法是使用Homebrew: brew install coreutils - 然后使用gtimeout 或者将您的PATH设置为使用timeout名称。 - RichVel
截至Ubuntu 20.04,似乎没有单独的“timeout”软件包。 “timeout”命令由“coreutils”提供。 - Keith Thompson

1

你可以执行 ping -t 10 google.com >nul

>nul 命令会将输出清除。所以,不会显示“64 BYTES FROM 123.45.67.8 BLAH BLAH BLAH”,而是在超时之前只会显示一个空白的新行。-t 标志可以更改为任何数字。


4
Ping只是一个样本。我想运行一个程序来收集一些指标。 - sheki
1
">nul" 是一个 Windows 的东西。在 macOS 上,这会实际创建一个名为 "nul" 的文件并将输出重定向到它。 - pxeger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接