如何使 bash 命令定期运行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17394356/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make a bash command run periodically?
提问by jerome
I want to execute a script and have it run a command every x minutes.
我想执行一个脚本并让它每 x 分钟运行一个命令。
Also any general advice on any resources for learning bash scripting could be really cool. I use Linux for my personal development work, so bash scripts are not totally foreign to me, I just haven't written any of my own from scratch.
此外,关于学习 bash 脚本的任何资源的任何一般建议都可能非常酷。我将 Linux 用于我的个人开发工作,所以 bash 脚本对我来说并不陌生,我只是没有从头开始编写任何自己的脚本。
回答by Gilles Quenot
If you want to run a command periodically, there's 3 ways :
如果您想定期运行命令,有 3 种方法:
- using the
crontab
command ex.* * * * * command
(run every minutes) - using a loop like :
while true; do ./my_script.sh; sleep 60; done
(not precise) - using systemd timer
- 使用
crontab
命令 ex。* * * * * command
(每分钟运行一次) - 使用像这样的循环:(
while true; do ./my_script.sh; sleep 60; done
不精确) - 使用系统定时器
See cron
见cron
Some pointers for best bash scripting practices :
一些关于最佳 bash 脚本实践的指针:
http://mywiki.wooledge.org/BashFAQ
Guide: http://mywiki.wooledge.org/BashGuide
ref: http://www.gnu.org/software/bash/manual/bash.html
http://wiki.bash-hackers.org/
USE MORE QUOTES!: http://www.grymtheitroade.com/Unix/Quote.html
Scripts and more: http://www.shelldorado.com/
http://mywiki.wooledge.org/BashFAQ
指南:http://mywiki.wooledge.org/BashGuide
REF:http://www.gnu.org/software/bash/manual/bash.html
HTTP://维基.bash-hackers.org/
使用更多引用!:http: //www.grymtheitroade.com/Unix/Quote.html
脚本和更多:http: //www.shelldorado.com/
回答by Yossarian
In addition to @sputnick's answer, there is also watch
. From the man page:
除了@sputnick 的回答,还有watch
. 从手册页:
Execute a program periodically, showing output full screen
By default this is every 2 seconds. watch
is useful for tail
ing logs, for example.
默认情况下,这是每 2 秒一次。例如,对 ing 日志watch
很有tail
用。
回答by mklement0
macOSusers: here's a partialimplementation of the GNU watch
command(as of version 0.3.0
) for interactiveperiodic invocations for primarily visualinspection:
macOS用户:这里是GNU命令的部分实现watch
(从 版本开始0.3.0
),用于主要进行视觉检查的交互式定期调用:
It is syntax-compatible with the GNU version and fails with a specific error message if an unimplemented feature is used.
它与 GNU 版本的语法兼容,如果使用了未实现的功能,则会失败并显示特定的错误消息。
Notable limitations:
显着限制:
- The output is not limited to one screenful.
- Displaying output differences is not supported.
- Using precise timing is not supported.
- Colored output is alwayspassed through (
--color
is implied).
- 输出不限于一屏。
- 不支持显示输出差异。
- 不支持使用精确计时。
- 彩色输出总是通过(
--color
是隐含的)。
Also implements a few non-standard features, such as waiting for success(-E
) to complement waiting for error (-e
) and showing the time of day of the last invocation as well as the total time elapsed so far.
还实现了一些非标准功能,例如等待成功( -E
) 以补充等待错误 ( -e
) 并显示上次调用的时间以及到目前为止所用的总时间。
Run watch -h
for details.
跑去watch -h
了解详情。
Examples:
例子:
watch -n 1 ls # list current dir every second
watch -e 'ls *.lockfile' # list lock files and exit once none exist anymore.
Source code(paste into a script file named watch
, make it executable, and place in a directory in your $PATH
; note that syntax highlighting here is broken, but the code works):
源代码(粘贴到名为 的脚本文件中watch
,使其可执行,并将其放置在您的 目录中$PATH
;请注意,此处的语法突出显示已损坏,但代码有效):
#!/usr/bin/env bash
THIS_NAME=$(basename "$BASH_SOURCE")
VERSION='0.1'
# Helper function for exiting with error message due to runtime error.
# die [errMsg [exitCode]]
# Default error message states context and indicates that execution is aborted. Default exit code is 1.
# Prefix for context is always prepended.
# Note: An error message is *always* printed; if you just want to exit with a specific code silently, use `exit n` directly.
die() {
echo "$THIS_NAME: ERROR: ${1:-"ABORTING due to unexpected error."}" 1>&2
exit ${2:-1} # Note: If the argument is non-numeric, the shell prints a warning and uses exit code 255.
}
# Helper function for exiting with error message due to invalid parameters.
# dieSyntax [errMsg]
# Default error message is provided, as is prefix and suffix; exit code is always 2.
dieSyntax() {
echo "$THIS_NAME: PARAMETER ERROR: ${1:-"Invalid parameter(s) specified."} Use -h for help." 1>&2
exit 2
}
# Get the elapsed time since the specified epoch time in format HH:MM:SS.
# Granularity: whole seconds.
# Example:
# tsStart=$(date +'%s')
# ...
# getElapsedTime $tsStart
getElapsedTime() {
date -j -u -f '%s' $(( $(date +'%s') - )) +'%H:%M:%S'
}
# Command-line help.
if [[ "" == '--help' || "" == '-h' ]]; then
cat <<EOF
SYNOPSIS
$THIS_NAME [-n seconds] [opts] cmd [arg ...]
DESCRIPTION
Executes a command periodically and displays its output for visual inspection.
NOTE: This is a PARTIAL implementation of the GNU \`watch\` command, for OS X.
Notably, the output is not limited to one screenful, and displaying
output differences and using precise timing are not supported.
Also, colored output is always passed through (--color is implied).
Unimplemented features are marked as [NOT IMPLEMENTED] below.
Conversely, features specific to this implementation are marked as [NONSTD].
Reference version is GNU watch 0.3.0.
CMD may be a simple command with separately specified
arguments, if any, or a single string containing one or more
;-separated commands (including arguments) - in the former case the command
is directly executed by bash, in the latter the string is passed to \`bash -c\`.
Note that GNU watch uses sh, not bash.
To use \`exec\` instead, specify -x (see below).
By default, CMD is re-invoked indefinitely; terminate with ^-C or
exit based on conditions:
-e, --errexit
exits once CMD indicates an error, i.e., returns a non-zero exit code.
-E, --okexit [NONSTD]
is the inverse of -e: runs until CMD returns exit code 0.
By default, all output is passed through; the following options modify this
behavior; note that suppressing output only relates to CMD's output, not the
messages output by this utility itself:
-q, --quiet [NONSTD]
suppresses stdout output from the command invoked;
-Q, --quiet-both [NONSTD]
suppresses both stdout and stderr output.
-l, --list [NONSTD]
list-style display; i.e., suppresses clearing of the screen
before every invocation of CMD.
-n secs, --interval secs
interval in seconds between the end of the previous invocation of CMD
and the next invocation - 2 seconds by default, fractional values permitted;
thus, the interval between successive invocations is the specified interval
*plus* the last CMD's invocation's execution duration.
-x, --exec
uses \`exec\` rather than bash to execute CMD; this requires
arguments to be passed to CMD to be specified as separate arguments
to this utility and prevents any shell expansions of these arguments
at invocation time.
-t, --no-title
suppresses the default title (header) that displays the interval,
and (NONSTD) a time stamp, the time elapsed so far, and the command executed.
-b, --beep
beeps on error (bell signal), i.e., when CMD reports a non-zero exit code.
-c, --color
IMPLIED AND ALWAYS ON: colored command output is invariably passed through.
-p, --precise [NOT IMPLEMENTED]
-d, --difference [NOT IMPLEMENTED]
EXAMPLES
# List files in home folder every second.
$THIS_NAME -n 1 ls ~
# Wait until all *.lockfile files disappear from the current dir, checking every 2 secs.
$THIS_NAME -e 'ls *.lockfile'
EOF
exit 0
fi
# Make sure that we're running on OSX.
[[ $(uname) == 'Darwin' ]] || die "This script is designed to run on OS X only."
# Preprocess parameters: expand compressed options to individual options; e.g., '-ab' to '-a -b'
params=() decompressed=0 argsReached=0
for p in "$@"; do
if [[ $argsReached -eq 0 && $p =~ ^-[a-zA-Z0-9]+$ ]]; then # compressed options?
decompressed=1
params+=(${p:0:2})
for (( i = 2; i < ${#p}; i++ )); do
params+=("-${p:$i:1}")
done
else
(( argsReached && ! decompressed )) && break
[[ $p == '--' || ${p:0:1} != '-' ]] && argsReached=1
params+=("$p")
fi
done
(( decompressed )) && set -- "${params[@]}"; unset params decompressed argsReached p # Replace "$@" with the expanded parameter set.
# Option-parameters loop.
interval=2 # default interval
runUntilFailure=0
runUntilSuccess=0
quietStdOut=0
quietStdOutAndStdErr=0
dontClear=0
noHeader=0
beepOnErr=0
useExec=0
while (( $# )); do
case "" in
--) # Explicit end-of-options marker.
shift # Move to next param and proceed with data-parameter analysis below.
break
;;
-p|--precise|-d|--differences|--differences=*)
dieSyntax "Sadly, option is NOT IMPLEMENTED."
;;
-v|--version)
echo "$VERSION"; exit 0
;;
-x|--exec)
useExec=1
;;
-c|--color)
# a no-op: unlike the GNU version, we always - and invariably - pass color codes through.
;;
-b|--beep)
beepOnErr=1
;;
-l|--list)
dontClear=1
;;
-e|--errexit)
runUntilFailure=1
;;
-E|--okexit)
runUntilSuccess=1
;;
-n|--interval)
shift; interval=;
errMsg="Please specify a positive number of seconds as the interval."
interval=$(bc <<<"") || dieSyntax "$errMsg"
(( 1 == $(bc <<<"$interval > 0") )) || dieSyntax "$errMsg"
[[ $interval == *.* ]] || interval+='.0'
;;
-t|--no-title)
noHeader=1
;;
-q|--quiet)
quietStdOut=1
;;
-Q|--quiet-both)
quietStdOutAndStdErr=1
;;
-?|--?*) # An unrecognized switch.
dieSyntax "Unrecognized option: ''. To force interpretation as non-option, precede with '--'."
;;
*) # 1st data parameter reached; proceed with *argument* analysis below.
break
;;
esac
shift
done
# Make sure we have at least a command name
[[ -n "" ]] || dieSyntax "Too few parameters specified."
# Suppress output streams, if requested.
# Duplicate stdout and stderr first.
# This allows us to produce output to stdout (>&3) and stderr (>&4) even when suppressed.
exec 3<&1 4<&2
if (( quietStdOutAndStdErr )); then
exec &> /dev/null
elif (( quietStdOut )); then
exec 1> /dev/null
fi
# Set an exit trap to ensure that the duplicated file descriptors are closed.
trap 'exec 3>&- 4>&-' EXIT
# Start loop with periodic invocation.
# Note: We use `eval` so that compound commands - e.g. 'ls; bash --version' - can be passed.
tsStart=$(date +'%s')
while :; do
(( dontClear )) || clear
(( noHeader )) || echo "Every ${interval}s. [$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)]: $@"$'\n' >&3
if (( useExec )); then
(exec "$@") # run in *subshell*, otherwise *this* script will be replaced by the process invoked
else
if [[ $* == *' '* ]]; then
# A single argument with interior spaces was provided -> we must use `bash -c` to evaluate it properly.
bash -c "$*"
else
# A command name only or a command name + arguments were specified as separate arguments -> let bash run it directly.
"$@"
fi
fi
ec=$?
(( ec != 0 && beepOnErr )) && printf '\a'
(( ec == 0 && runUntilSuccess )) && { echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: exit code 0 reported." >&3; exit 0; }
(( ec != 0 && runUntilFailure )) && { echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: non-zero exit code ($ec) reported." >&3; exit 0; }
sleep $interval
done
回答by SheetJS
I want to execute the script and have it run a command every {time interval}
我想执行脚本并让它每隔 {time interval} 运行一个命令
cron
(https://en.wikipedia.org/wiki/Cron) was designed for this purpose. If you run man cron
or man crontab
you will find instructions for how to use it.
cron
( https://en.wikipedia.org/wiki/Cron) 就是为此目的而设计的。如果您运行man cron
orman crontab
您会找到有关如何使用它的说明。
any general advice on any resources for learning bash scripting could be really cool. I use Linux for my personal development work, so bash scripts are not totally foreign to me, I just haven't written any of my own from scratch.
关于学习 bash 脚本的任何资源的任何一般建议都可能非常酷。我将 Linux 用于我的个人开发工作,所以 bash 脚本对我来说并不陌生,我只是没有从头开始编写任何自己的脚本。
If you are comfortable working with bash, I recommend reading through the bash manpage first (man bash
) -- there are lots of cool tidbits.
如果您习惯使用 bash,我建议您先通读 bash 联机帮助页 ( man bash
) —— 有很多很酷的花絮。
回答by David H.
Avoiding Time Drift
避免时间漂移
Here's what I do to remove the time it takes for the command to run and still stay on schedule:
以下是我为消除命令运行所需的时间而仍按计划执行的操作:
#One-liner to execute a command every 600 seconds avoiding time drift
#Runs the command at each multiple of :10 minutes
while sleep $(echo 600-`date "+%s"`%600 | bc); do ls; done
This will drift off by no more than one second. Then it will snap back in sync with the clock. If you need something with less than 1 second drift and your sleep command supports floating point numbers, try adding including nanoseconds in the calculation like this
这将漂移不超过一秒钟。然后它将与时钟同步回弹。如果您需要少于 1 秒的漂移并且您的 sleep 命令支持浮点数,请尝试在计算中添加包括纳秒,如下所示
while sleep $(echo 6-`date "+%s.%N"`%6 | bc); do date '+%FT%T.%N'; done
回答by KiriSakow
If you need to visually monitor a command which gives a static output, use
watch [options] command
. For example, for monitoring free memory, run:watch -n 1 free -m
where the
-n 1
option sets update interval to 1 second (default is 2 seconds).
Checkman watch
or the online manualfor details.
如果您需要直观地监视提供静态输出的命令,请使用
watch [options] command
. 例如,要监控空闲内存,请运行:watch -n 1 free -m
其中该
-n 1
选项将更新间隔设置为 1 秒(默认为 2 秒)。
详情请查看man watch
或在线手册。
If you need to visually monitor changes in a log file,
tail
is your command of choice, for example:tail -f /path/to/logs/file.log
where the
-f
(for “follow”) option tells the program to output appended data as the file grows.
Checkman tail
or the online manualfor details.
如果您需要直观地监视日志文件中的更改,
tail
您可以选择以下命令,例如:tail -f /path/to/logs/file.log
其中
-f
(for “follow”) 选项告诉程序随着文件的增长输出附加数据。
详情请查看man tail
或在线手册。