Bash 执行具有超时的shell函数
为什么会这样Bash 执行具有超时的shell函数,bash,function,shell,timeout,Bash,Function,Shell,Timeout,为什么会这样 timeout 10s echo "foo bar" # foo bar 但这不会 function echoFooBar { echo "foo bar" } echoFooBar # foo bar timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory 我该如何使它工作呢?timeout是一个命令,因此它在bash shell的子
timeout 10s echo "foo bar" # foo bar
但这不会
function echoFooBar {
echo "foo bar"
}
echoFooBar # foo bar
timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory
我该如何使它工作呢?
timeout
是一个命令,因此它在bash shell的子进程中执行。因此,它无法访问当前shell中定义的函数
给定的命令timeout
作为timeout的子进程执行,timeout是shell的一个子进程
您可能会感到困惑,因为echo
既是一个shell内置命令,也是一个单独的命令
您可以做的是将函数放入它自己的脚本文件中,chmod它为可执行文件,然后使用timeout
执行它
或者,在子shell中执行您的函数,并在原始进程中监视进度,如果花费的时间太长,则终止子进程。如果您只想将超时作为整个现有脚本的附加选项,可以让它测试超时选项,然后让它在没有这个选项的情况下自己递归地调用它 example.sh:
#!/bin/bash
if [ "$1" == "-t" ]; then
timeout 1m $0 $2
else
#the original script
echo $1
sleep 2m
echo YAWN...
fi
在不超时的情况下运行此脚本:
$./example.sh -other_option # -other_option
# YAWN...
以一分钟的超时时间运行它:
$./example.sh -t -other_option # -other_option
还有一种内联的替代方案,也可以启动bash shell的子流程:
timeout 10s bash <<EOT
function echoFooBar {
echo foo
}
echoFooBar
sleep 20
EOT
timeout 10s bash您可以创建一个函数,该函数允许您执行与timeout相同的操作,但也可以用于其他函数:
function run_cmd {
cmd="$1"; timeout="$2";
grep -qP '^\d+$' <<< $timeout || timeout=10
(
eval "$cmd" &
child=$!
trap -- "" SIGTERM
(
sleep $timeout
kill $child 2> /dev/null
) &
wait $child
)
}
注意:解决方案来自我的一个问题:
函数foo(){
对于{1..100}中的i;
做
echo$i;
睡眠1;
完成;
}
cat正如道格拉斯·利德(Douglas Leeder)所说,您需要一个单独的超时进程来向用户发送信号。解决方法是将函数导出到子shell并手动运行子shell
export -f echoFooBar
timeout 10s bash -c echoFooBar
此函数仅使用内置项
>P>可以考虑评估“$*”,而不是直接根据您的需要运行$@ < /p>
- 它使用在第一个参数(超时值)之后指定的命令字符串启动作业,并监视作业pid
- 它每1秒检查一次,bash支持0.01的超时,因此可以调整
- 另外,如果您的脚本需要stdin,
read
应该依赖一个专用的fd(exec{tofd}将我对Tiago Lopo答案的评论转换成更可读的形式:
我认为在最新的子shell上设置一个超时更具可读性,这样我们就不需要求值字符串,整个脚本就可以由您最喜欢的编辑器突出显示为shell。我只需在带有eval
的子shell生成一个shell函数(使用zsh进行测试,但应该与bash一起使用)之后,将命令放入其中:
timeout\u子项(){
陷阱--“SIGTERM”
child=$!
超时=$1
(
睡眠$超时
杀死$child
) &
等等$child
}
用法示例:
(如果为true,则执行echo-n;睡眠0.1;完成)&timeout\u child 2
这样,它也可以与shell函数一起工作(如果它在后台运行):
打印点(){
虽然是真的
做
睡眠0.1
echo-n。
完成
}
>打印点和超时\u子项2
[1] 21725
[3] 21727
………[1]21725个终止打印点
[3] +21727完成(睡眠$timeout;杀死$child;)
我对@Tiago Lopo的答案做了一点修改,可以处理带有多个参数的命令。我还测试了TauPan的解决方案,但如果在脚本中多次使用它,它就不起作用,而Tiago的解决方案则起作用
函数超时\u cmd{
本地arr
本地命令
本地超时
arr=(“$@”)
#超时:第一个参数
#cmd:其他参数
timeout=“${arr[0]}”
cmd=(“${arr[@]:1}”)
(
评估“${cmd[@]}”&
child=$!
echo“child:$child”
陷阱--“SIGTERM”
(
睡眠“$timeout”
杀死“$child”2>/dev/null
) &
等待“$child”
)
}
下面是一个功能完整的脚本,您可以使用它来测试上述功能:
$。/test\u timeout.sh-h
用法:
测试\u timeout.sh[-n][-r REPEAT][s SLEEP\u TIME][-t timeout]
test_timeout.sh-h
测试超时\u cmd函数。
选项:
-n干跑时,不要实际睡觉。
-r重复多次重新复制所有内容[默认值:1]。
-s SLEEP_TIME SLEEP for SLEEP_TIME seconds[默认值:5]。
-超时秒后超时[默认值:无超时]。
例如,您可以这样启动cnal:
$。/test\u timeout.sh-r2-s5-t3
试试看:1
-将超时设置为:3
儿童:2540
->检索:143
->命令超时了
试试看:2
-将超时设置为:3
儿童:2593
->检索:143
->命令超时了
完成!
!/usr/bin/env bash
#shellcheck禁用=SC2128
sourceed=false&[“$0”=“$BASH_SOURCE”]| | sourceed=true
如果!那么
设置-euo管道故障
IFS=$'\n\t'
fi
####################助手
函数检查{
本地re='^[0-9]+$'
本地mynum=“$1”
本地选项=“$2”
如果![“$mynum”=~$re]];那么
(echo-n“选项“$option”中的错误:”>&2)
(echo“必须是正整数,获得$mynum。”>&2)
出口1
fi
如果![“$mynum”-gt 0];那么
(echo“选项“$option”中的错误”:必须为正,获得$mynum。“>&2)
出口1
fi
}
####################完:助理
####################用法
函数short_用法(){
(>&2)回音\
“用法:
测试\u timeout.sh[-n][-r REPEAT][s SLEEP\u TIME][-t timeout]
测试_timeout.sh-h“
)
}
函数用法(){
(>&2短_用法)
(>&2)回音\
"
测试超时\u cmd函数。
选项:
-n干跑时,不要实际睡觉。
-r重复多次重新复制所有内容[默认值:1]。
-s SLEEP_TIME SLEEP for SLEEP_TIME seconds[默认值:5]。
-超时秒后超时[默认值:无超时]。
")
}
####################完:用法
帮助标志=false
dryrun_标志=错误
睡眠时间=5
超时=-1
重复=1
而getopts“:hnr:s:t:“opt;do
案例$opt-in
h)
帮助标志=true
;;
n)
dryrun_标志=真
;;
(r)
勾选“$OPTAR”
function foo(){
for i in {1..100};
do
echo $i;
sleep 1;
done;
}
cat <( foo ) # Will work
timeout 3 cat <( foo ) # Will Work
timeout 3 cat <( foo ) | sort # Wont work, As sort will fail
cat <( timeout 3 cat <( foo ) ) | sort -r # Will Work
export -f echoFooBar
timeout 10s bash -c echoFooBar
## forking is evil
timeout() {
to=$1; shift
$@ & local wp=$! start=0
while kill -0 $wp; do
read -t 1
start=$((start+1))
if [ $start -ge $to ]; then
kill $wp && break
fi
done
}
$ TMOUT=10 && echo "foo bar"