本文目录导读:

Shell脚本中实现超时控制有多种方法,以下是几种常见的实现方式:
使用 timeout 命令(最推荐)
# 基本用法:timeout [时间] 命令
timeout 5s sleep 10 # 5秒后终止sleep命令
echo "命令已超时或完成"
# 带错误处理
if timeout 5s sleep 10; then
echo "命令在5秒内完成"
else
echo "命令超时或被中断"
fi
# 使用不同的时间单位
timeout 5s # 5秒
timeout 1m # 1分钟
timeout 1h # 1小时
timeout 1d # 1天
# 发送特定信号
timeout -s SIGKILL 5s sleep 10 # 使用SIGKILL信号
使用 expect 工具
#!/bin/bash
# 使用expect实现超时控制
expect << EOF
set timeout 10 # 设置10秒超时
spawn long_running_command
expect {
timeout { puts "命令超时"; exit 1 }
eof { puts "命令完成"; exit 0 }
}
EOF
使用后台进程 + 定时器
#!/bin/bash
# 通用超时函数
run_with_timeout() {
local timeout=$1
shift
local cmd="$@"
# 在后台执行命令
( $cmd ) &
local cmd_pid=$!
# 在后台执行超时监控
( sleep $timeout; kill $cmd_pid 2>/dev/null ) &
local timeout_pid=$!
# 等待命令完成
wait $cmd_pid 2>/dev/null
local result=$?
# 杀掉超时进程(如果已经完成)
kill $timeout_pid 2>/dev/null
return $result
}
# 使用示例
run_with_timeout 5 "sleep 10"
if [ $? -eq 0 ]; then
echo "命令在超时前完成"
else
echo "命令超时"
fi
使用 read 命令的超时功能
#!/bin/bash
# 等待用户输入,带超时
echo "请在5秒内输入:"
if read -t 5 user_input; then
echo "收到输入: $user_input"
else
echo "超时"
fi
# 等待进程完成,带超时
timeout_read() {
local timeout=$1
local pid=$2
# 循环检查进程状态
while [ $timeout -gt 0 ]; do
if ! kill -0 $pid 2>/dev/null; then
return 0 # 进程正常结束
fi
sleep 1
((timeout--))
done
kill $pid 2>/dev/null
return 1 # 超时
}
使用管道实现输出捕获
#!/bin/bash
run_with_timeout_and_capture() {
local timeout=$1
local output=""
shift
local cmd="$@"
# 创建临时文件存储输出
local tmpfile=$(mktemp)
# 执行命令并捕获输出
( $cmd > "$tmpfile" 2>&1 ) &
local cmd_pid=$!
# 等待指定时间
if ! wait $cmd_pid 2>/dev/null
then
# 如果还运行则杀掉
kill $cmd_pid 2>/dev/null
echo "命令超时" >&2
cat "$tmpfile"
rm -f "$tmpfile"
return 1
fi
# 读取输出
output=$(cat "$tmpfile")
rm -f "$tmpfile"
echo "$output"
return 0
}
# 使用示例
result=$(run_with_timeout_and_capture 5 "sleep 3 && echo '完成'")
echo "结果: $result"
完整的超时控制函数库
#!/bin/bash
# 超时控制函数库
TIMEOUT_LIB_VERSION="1.0"
# 检查超时是否可用
check_timeout_available() {
command -v timeout &>/dev/null
}
# 通用超时执行函数
execute_with_timeout() {
local timeout=$1
local default_signal=${2:-SIGTERM}
shift 2
local cmd="$@"
# 优先使用timeout命令
if check_timeout_available; then
timeout -s $default_signal $timeout $cmd
return $?
fi
# 备选方案:手动实现
(
$cmd &
local cmd_pid=$!
# 启动超时监控
( sleep $timeout; kill -$default_signal $cmd_pid 2>/dev/null ) &
local timer_pid=$!
wait $cmd_pid 2>/dev/null
local result=$?
# 清理定时器
kill $timer_pid 2>/dev/null
exit $result
)
return $?
}
# 带进度提示的超时执行
execute_with_progress() {
local timeout=$1
local message=${2:-"执行中"}
shift 2
local cmd="$@"
$cmd &
local cmd_pid=$!
local count=0
while [ $count -lt $timeout ]; do
if ! kill -0 $cmd_pid 2>/dev/null; then
echo -e "\n命令完成"
wait $cmd_pid
return $?
fi
echo -n "$message."
sleep 1
((count++))
done
echo -e "\n超时,终止命令"
kill $cmd_pid 2>/dev/null
return 124
}
# 使用示例
echo "===== 测试1: 使用timeout命令 ====="
if execute_with_timeout 3 "sleep 5"; then
echo "命令完成"
else
echo "命令超时"
fi
echo -e "\n===== 测试2: 带进度提示 ====="
execute_with_progress 5 "正在执行" "sleep 10"
特殊场景:ping命令的超时
#!/bin/bash # ping命令自带超时选项 ping -c 3 -W 5 8.8.8.8 # 3个包,每个超时5秒 # 或者使用-w选项作为全局超时 ping -c 3 -w 10 8.8.8.8 # 整体超时10秒
选择建议
- 标准Linux环境:优先使用
timeout命令 - 兼容性要求高:使用后台进程+定时器方法
- 需要交互式控制:使用
expect工具 - 简单场景:使用
read -t或进程状态检查
每种方法都有其适用场景,选择时考虑:
- 系统兼容性(timeout命令需要coreutils)
- 是否需要捕获输出
- 是否需要处理特定信号
- 是否需要进度提示