bash循环需要很长时间

时间:2017-03-17 15:35:03

标签: bash

我有一个时间列表,我以HH:MM:SS格式循环,以找到最近但未过去的时间。我的代码是:

 for i in ${times[@]}; do
    hours=$(echo $i | sed 's/\([0-9]*\):.*/\1/g')
    minutes=$(echo $i | sed 's/.*:\([0-9]*\):.*/\1/g')
    currentHours=$(date +"%H")
    currentMinutes=$(date +"%M")
    if [[ hours -ge currentHours ]]; then
            if [[ minutes -ge currentMinutes ]]; then
            break
            fi
    fi
done

变量times是我正在排序的所有时间的数组(大约20-40行)。我预计这个时间不到1秒,但需要花费5秒钟。任何减少正则表达时间的建议都将受到赞赏。

times=($(cat file.txt)) 

以下是存储在文本文件中的时间列表,并使用上面的代码行导入时间变量。

6:05:00
6:35:00
7:05:00
7:36:00
8:08:00
8:40:00
9:10:00
9:40:00
10:11:00
10:41:00
11:11:00
11:41:00
12:11:00
12:41:00
13:11:00
13:41:00
14:11:00
14:41:00
15:11:00
15:41:00
15:56:00
16:11:00
16:26:00
16:41:00
16:58:00
17:11:00
17:26:00
17:41:00
18:11:00
18:41:00
19:10:00
19:40:00
20:10:00
20:40:00
21:15:00
21:45:00

3 个答案:

答案 0 :(得分:4)

从性能角度看bash脚本时要理解的一个关键事项是,虽然bash解释器有点慢,但产生外部进程的行为极其慢。因此,虽然它通常可以加快脚本使用awksed的单个调用来处理大量输入,但在紧密循环内启动这些调用将大大超过这些工具的性能一旦他们开始运作。

任何命令替换 - $() - 导致解释器的第二个副本作为子shell被fork()删除。调用未构建在bash中的任何命令 - datesed等 - 然后导致该进程的子进程被fork()删除,然后与该进程关联的可执行文件是exec()' d - 涉及大量操作系统级开销(二进制文件需要链接,加载等)。

这个循环最好写成:

IFS=: read -r currentHours currentMinutes < <(date +"%H:%M")
while IFS=: read -r hours minutes _; do
    if (( hours >= currentHours )) && (( minutes >= currentMinutes )); then
        break
    fi
done <file.txt

在此表单中,只运行一个外部命令,date +"%H:%M"外部循环。如果你只针对bash 4.2及更新版本(内置时间格式支持),那么即使这样也是不必要的:

printf -v currentHours '%(%H)T' -1
printf -v currentMinutes '%(%M)T' -1

...将仅使用现代bash版本中内置的功能将当前小时和分钟直接放入变量currentHourscurrentMinutes

请参阅:

  • BashFAQ #1 - 如何逐行(和/或逐字段)读取文件(数据流,变量)?
  • BashFAQ #100 - 如何在bash中进行原生字符串操作? (小节:&#34;将字符串拆分为字段&#34;

答案 1 :(得分:2)

说实话,我不确定为什么它会花费很长时间,但肯定会有一些事情可以提高效率。

currentHours=$(date +"%H")
currentMinutes=$(date +"%M")

for time in "${times[@]}"; do
    IFS=: read -r hours minutes seconds <<<"$time"
    if [[ hours -ge currentHours && minutes -ge currentMinutes ]]; then
        break
    fi
done

这使用内置命令read将文本拆分为变量,而不是调用外部命令和创建子shell。

我认为您希望脚本运行得如此之快,以至于在循环中重用currentHourscurrentMinutes是安全的。

请注意,您也可以使用awk来完成整个事情:

awk -F: -v currentHours="$(date +"%H") -v currentMinutes="$(date +"%M")" '
    $1 >= currentHours && $2 >= currentMinutes { print; exit }' file.txt

为了让程序产生一些输出,我添加了print,以便打印最后一行。

答案 2 :(得分:1)

awk救援!

 awk -v time="12:12:00" '
           function pad(x) {split(x,ax,":"); return (ax[1]<10)?"0"x:x}
                     BEGIN {time=pad(time)}
              time>pad($0) {next} 
                           {print; exit}' times


12:41:00

用0填充小时,你只能进行字符串比较。