dre*_*ves 564 command-line scripting
有一个内置的 Unix 命令,repeat它的第一个参数是重复命令的次数,其中命令(带有任何参数)由 的其余参数指定repeat。
例如,
% repeat 100 echo "I will not automate this punishment."
Run Code Online (Sandbox Code Playgroud)
将回显给定的字符串 100 次,然后停止。
我想要一个类似的命令——让我们称之为forever——它的工作原理类似,除了第一个参数是重复之间暂停的秒数,它会永远重复。例如,
% forever 5 echo "This will get echoed every 5 seconds forever and ever."
Run Code Online (Sandbox Code Playgroud)
我想我会在写之前问这样的事情是否存在。我知道它就像一个 2 行的 Perl 或 Python 脚本,但也许有更标准的方法来做到这一点。如果没有,请随时用您喜欢的脚本语言发布解决方案。
PS:也许更好的方法是将repeat重复次数(-1 表示无穷大)和重复之间的睡眠秒数进行概括。上面的例子将变成:
% repeat 100 0 echo "I will not automate this punishment."
% repeat -1 5 echo "This will get echoed every 5 seconds forever."
Run Code Online (Sandbox Code Playgroud)
小智 719
试试这个watch命令。
Usage: watch [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>]
[--no-title] [--version] <command>`
Run Code Online (Sandbox Code Playgroud)
以便:
watch -n1 command
Run Code Online (Sandbox Code Playgroud)
将每秒运行该命令(好吧,从技术上讲,每一秒加上command运行所需的时间watch(至少procps和busybox实现)在两次运行之间只睡一秒钟command),永远。
您是否想将命令传递给exec而不是sh -c,使用-x选项:
watch -n1 -x command
Run Code Online (Sandbox Code Playgroud)
在 macOS 上,您可以watch从Mac Ports获取:
port install watch
Run Code Online (Sandbox Code Playgroud)
或者您可以从Homebrew获取它:
brew install watch
Run Code Online (Sandbox Code Playgroud)
小智 300
重击
while+ sleep:
while true
do
echo "Hi"
sleep 1
done
Run Code Online (Sandbox Code Playgroud)
这与速记单行代码相同(来自下面的评论):
while sleep 1; do echo "Hi"; done
Run Code Online (Sandbox Code Playgroud)
用于;分隔命令和sleep 1用于while测试的用途,因为它始终返回 true。您可以在循环中放置更多命令 - 只需将它们与;
小智 86
这只是其他while+sleep答案的简短版本,如果您经常将此类任务作为日常工作运行,那么使用它可以避免不必要的按键操作,并且如果您的命令行开始变得更长,理解这个会更容易一些。但这个是从先睡觉开始的。
如果您需要遵循诸如机器负载之类的单行输出,这通常很有用:
while sleep 1; do uptime; done
Run Code Online (Sandbox Code Playgroud)
Kei*_*son 48
到目前为止发布的所有答案都存在一个问题,即执行命令的时间可能会漂移。例如,如果您sleep 10在命令之间执行一个命令,并且该命令需要运行 2 秒,那么它将每 12 秒运行一次;如果运行所需的时间可变,那么从长远来看,它运行的时间是不可预测的。
这可能正是您想要的;如果是这样,请使用其他解决方案之一,或使用此解决方案但简化sleep呼叫。
对于一分钟的解决方案,cron 作业将在指定的时间运行,无论每个命令需要多长时间。(事实上,即使前一个任务仍在运行,新的 cron 任务也会启动。)
这是一个简单的 Perl 脚本,它会休眠直到下一个间隔,例如,如果间隔为 10 秒,命令可能会在 12:34:00、12:34:10、12:34:20 等运行,即使命令本身需要几秒钟。如果命令运行时间超过interval秒,则将跳过下一个时间间隔(与 cron 不同)。该间隔是相对于纪元计算的,因此 86400 秒(1 天)的间隔将在 UTC 午夜运行。
#!/usr/bin/perl
use strict;
use warnings;
if (scalar @ARGV < 2) {
die "Usage: $0 seconds command [args...]\n";
}
$| = 1; # Ensure output appears
my($interval, @command) = @ARGV;
# print ">>> interval=$interval command=(@command)\n";
while (1) {
print "sleep ", $interval - time % $interval, "\n";
sleep $interval - time % $interval;
system @command; # TODO: Handle errors (how?)
}
Run Code Online (Sandbox Code Playgroud)
swa*_*log 40
我认为到目前为止这里的所有答案要么太复杂,要么回答一个不同的问题:
真正的问题是:
当命令需要时间来完成时,这是两件非常不同的事情。
以脚本为例foo.sh(假设这是一个需要几秒钟才能完成的程序)。
#!/bin/bash
# foo.sh
echo `date +"%H:%M:%S"` >> output.txt;
sleep 2.5;
# ---
Run Code Online (Sandbox Code Playgroud)
您希望每秒运行一次,大多数人会建议watch -n1 ./foo.sh, 或while sleep 1; do ./foo.sh; done。但是,这给出了输出:
15:21:20
15:21:23
15:21:27
15:21:30
15:21:34
Run Code Online (Sandbox Code Playgroud)
这并不是每秒都在运行。即使有-p标志,正如man watch页面所建议的那样可能会解决这个问题,结果是一样的。
完成所需任务的一种简单方法是在后台运行命令。换句话说:
while sleep 1; do (./foo.sh &) ; done
Run Code Online (Sandbox Code Playgroud)
这就是它的全部内容。
你可以每 500 毫秒运行一次sleep 0.5,或者你有什么。
小智 17
在bash:
bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; done'
Run Code Online (Sandbox Code Playgroud)
(echo可以用任何命令替换...
或在perl:
perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"}'
Run Code Online (Sandbox Code Playgroud)
Whereprint "I will not automate this punishment in absurdum.\n"可以替换为用反引号 (`) 括起来的“any”命令。
对于暂停,sleep在 for 循环中添加一条语句:
bash -c 'while [ 0 ]; do echo "I will not automate this punishment in absurdum."; sleep 1; done'
Run Code Online (Sandbox Code Playgroud)
和
perl -e 'for (;1;) {print "I will not automate this punishment in absurdum.\n"; sleep 1}'
Run Code Online (Sandbox Code Playgroud)
F. *_*uri 12
为了限制执行时间,没有分叉!仅使用内置。
为此,我使用read内置函数而不是sleep. 不幸的是,这不适用于notty会话。
repeat" 根据要求:repeat () {
local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep
read -t .0001 repeat_foo
if [ $? = 1 ] ;then
repeat_sleep() { sleep $1 ;}
else
repeat_sleep() { read -t $1 repeat_foo; }
fi
shift 2
while ((repeat_times)); do
((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
"${@}"
((repeat_times))&& ((10#${repeat_delay//.})) &&
repeat_sleep $repeat_delay
done
}
Run Code Online (Sandbox Code Playgroud)
带引号的字符串的小测试:
repeat 3 0 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
Now: 15:13:43, Hello Guy.
repeat -1 .5 printf "Now: %(%T)T, Hello %s.\n" -1 Guy
Now: 15:14:14, Hello Guy.
Now: 15:14:14, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:15, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:16, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:17, Hello Guy.
Now: 15:14:18, Hello Guy.
Now: 15:14:18, Hello Guy.
^C
Run Code Online (Sandbox Code Playgroud)
在最近的 Linux 内核下,有一个 procfile/proc/timer_list包含以纳秒为单位的时间信息。
如果您想精确地每秒运行一次命令,您的命令必须在不到一秒的时间内结束!并从那里,你必须sleep只剩余电流第二。
如果延迟更重要并且您的命令不需要大量时间,您可以:
command=(echo 'Hello world.')
delay=10
while :;do
printf -v now "%(%s)T" -1
read -t $(( delay-(now%delay) )) foo
${command[@]}
done.
Run Code Online (Sandbox Code Playgroud)
但是,如果您的目标是获得更精细的粒度,则必须:
为此,我编写了一个小 bash 函数:
# bash source file for nano wait-until-next-second
mapfile </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
((_c+=${#_timer_list[_i]}))
[[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
[[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
waitNextSecondHires() {
local nsnow nsslp
read -N$TIMER_LIST_READ nsnow </proc/timer_list
nsnow=${nsnow%% nsecs*}
nsnow=$((${nsnow##* }+TIMER_LIST_OFFSET))
nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
read -t .${nsslp:1} foo
}
Run Code Online (Sandbox Code Playgroud)
采购它们后,您可以:
command=(echo 'Hello world.')
while :;do
waitNextSecondHires
${command[@]}
done.
Run Code Online (Sandbox Code Playgroud)
${command[@]}直接在命令行上运行,而不是比较
command=(eval "echo 'Hello world.';sleep .3")
while :;do
waitNextSecondHires
${command[@]}
done.
Run Code Online (Sandbox Code Playgroud)
这必须给出完全相同的结果。
repeat”:你可以参考这个:
mapfile </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
((_c+=${#_timer_list[_i]}))
[[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_READ=$_c
[[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
break
done
unset _i _timer_list _c
readonly TIMER_LIST_OFFSET TIMER_LIST_READ
repeat_hires () {
local repeat_times=$1 repeat_delay=$2 repeat_foo repeat_sleep repeat_count
read -t .0001 repeat_foo
if [ $? = 1 ] ;then
repeat_sleep() { sleep $1 ;}
else
repeat_sleep() { read -t $1 repeat_foo; }
fi
shift 2
printf -v repeat_delay "%.9f" $repeat_delay
repeat_delay=${repeat_delay//.}
read -N$TIMER_LIST_READ nsnow </proc/timer_list
nsnow=${nsnow%% nsec*}
started=${nsnow##* }
while ((repeat_times)); do
((repeat_times=repeat_times>0?repeat_times-1:repeat_times))
"${@}"
((repeat_times)) && ((10#$repeat_delay)) && {
read -N$TIMER_LIST_READ nsnow </proc/timer_list
nsnow=${nsnow%% nsec*}
nsnow=${nsnow##* }
(( (nsnow - started) / 10#$repeat_delay - repeat_count++ )) &&
printf >&2 "WARNING: Command '%s' too long for %f delay.\n" \
"${*}" ${repeat_delay:0:${#repeat_delay}-9
}.${repeat_delay:${#repeat_delay}-9}
printf -v sleep "%010d" $((
10#$repeat_delay - ( ( nsnow - started ) % 10#$repeat_delay ) ))
repeat_sleep ${sleep:0:${#sleep}-9}.${sleep:${#sleep}-9}
}
done
}
Run Code Online (Sandbox Code Playgroud)
那就试试吧:
time repeat_hires 21 .05 sh -c 'date +%s.%N;sleep .01'
1480867565.152022457
1480867565.201249108
1480867565.251333284
1480867565.301224905
1480867565.351236725
1480867565.400930482
1480867565.451207075
1480867565.501212329
1480867565.550927738
1480867565.601199721
1480867565.651500618
1480867565.700889792
1480867565.750963074
1480867565.800987954
1480867565.853671458
1480867565.901232296
1480867565.951171898
1480867566.000917199
1480867566.050942638
1480867566.101171249
1480867566.150913407
real 0m1.013s
user 0m0.000s
sys 0m0.016s
time repeat_hires 3 .05 sh -c 'date +%s.%N;sleep .05'
1480867635.380561067
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.486503367
WARNING: Command 'sh -c date +%s.%N;sleep .05' too long for 0.050000 delay.
1480867635.582332617
real 0m0.257s
user 0m0.000s
sys 0m0.004s
Run Code Online (Sandbox Code Playgroud)
如果您的目的不是在屏幕上显示消息,并且您有能力在几分钟内重复执行该工作,那么crontab也许将是您最好的工具。例如,如果您希望每分钟执行一次命令,您可以在crontab文件中编写如下内容:
* * * * * my_precious_command
Run Code Online (Sandbox Code Playgroud)
请查看教程以获取更多示例。此外,您可以使用Crontab Code Generator轻松设置时间。
Bash 及其一些类似的 shell 具有方便的(( ... ))表示法,可以对算术表达式进行求值。
因此,作为第三个挑战的答案,重复计数和每次重复之间的延迟都应该是可配置的,这是一种方法:
repeat=10
delay=1
i=0
while (( i++ < repeat )); do
echo Repetition $i
sleep $delay
done
Run Code Online (Sandbox Code Playgroud)
这个答案还受到基思答案中涵盖的时间漂移的影响。