【问题标题】:bash loop between two given dates两个给定日期之间的bash循环
【发布时间】:2010-12-10 09:13:04
【问题描述】:

我正在尝试创建一个脚本,该脚本将遍历文件名以下列格式编写的文件:yyyymmdd.hh.filename。

脚本被调用:

./loopscript.sh 20091026.00 23
./loopscript.sh 20091026.11 15
./loopscript.sh 20091026.09 20091027.17

脚本需要检查这两个给定日期/小时之间的每个小时。

例如

cat 20091026.00.filename |more
cat 20091026.01.filename |more
...
cat 20091026.23.filename |more
cat 20091027.01.filename |more
cat 20091027.02.filename |more
...

等等。

知道该怎么做吗?我对标准的 0 - x 循环没有任何困难。或简单的 for 循环。只是不确定如何处理上述问题。

【问题讨论】:

  • 无用cat:就用more filename

标签: bash loops


【解决方案1】:

这个怎么样:

#!/bin/bash

date1=$1
date2=$2

#verify dates
if ! date -d "$date1" 2>&1 > /dev/null ; 
    then echo "first date is invalid" ; exit 1
fi
if ! date -d "$date2" 2>&1 > /dev/null ; 
    then echo "second date is invalid" ; exit 1
fi

#set current and end date
current=$(date -d "$date1")
end=$(date -d "$date2 +1 hours")

#loop over all dates
while [ "$end" != "$current" ] 
do
    file=$(date -d "$current" +%Y%m%d.%H)
    cat $file."filename" | more
    current=$(date -d "$current +1 hours")
done

【讨论】:

  • 这对于日期来说是合理的,但根本不处理时间。实际上,日期也有点可疑 - 如果你给它 args 20091027 和 20091028,你会得到从 20091027.00 到 20091028.00 - 我希望它停止在 1027.23 或 1028.23。
  • 如果你用“20091027 00”“20091028 03”调用它,它确实可以处理几个小时,忘了提。
  • 啊啊,这样更好。大概这就是做繁重工作的日期。还不错。
  • 您可以使用 date 的返回值而不是 grepping 来获取错误消息:if ! date -d "$date1" >/dev/null 2>&1; then echo "first date is invalid"; exit 1; fi 和 x 在 while 语句中不是必需的。也无用cat
  • 感谢您的提示,更新了我的代码。但我不明白你的意思 cat 有什么用处,用户明确希望脚本“cat 20091026.00.filename |more”
【解决方案2】:

要在两个给定日期/小时之间处理每个文件,您可以使用以下命令:

#!/usr/bin/bash
#set -x

usage() {
    echo 'Usage: loopscript.sh <from> <to>'
    echo '       <from> MUST be yyyymmdd.hh or empty, meaning 00000000.00'
    echo '       <to> can be shorter and is affected by <from>'
    echo '         e.g., 20091026.00       27.01 becomes'
    echo '               20091026.00 20091027.01'
    echo '         If empty, it is set to 99999999.99'
    echo 'Arguments were:'
    echo "   '${from}'"
    echo "   '${to}'"
}

# Check parameters.

from="00000000.00"
to="99999999.99"
if [[ ! -z "$1" ]] ; then
    from=$1
fi
if [[ ! -z "$2" ]] ; then
    to=$2
fi
## Insert this to default to rest-of-day when first argument
##    but no second argument. Basically just sets second
##    argument to 23 so it will be transformed to end-of-day.
#if [[ ! -z "$1"]] ; then
#    if [[ -z "$2"]] ; then
#        to=23
#    fi
#fi

if [[ ${#from} -ne 11 || ${#to} -gt 11 ]] ; then
    usage
    exit 1
fi

# Sneaky code to modify a short "to" based on the start of "from".
# ${#from} is the length of ${from}.
# $((${#from}-${#to})) is the length difference between ${from} and ${to}
# ${from:0:$((${#from}-${#to}))} is the start of ${from} long enough
#   to make ${to} the same length.
# ${from:0:$((${#from}-${#to}))}${to} is that with ${to} appended.
# Voila! Easy, no?

if [[ ${#to} -lt ${#from} ]] ; then
    to=${from:0:$((${#from}-${#to}))}${to}
fi

# Process all files, checking that they're inside the range.

echo "From ${from} to ${to}"
for file in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].[0-9][0-9].* ; do
    if [[ ! ( ${file:0:11} < ${from} || ${file:0:11} > ${to} ) ]] ; then
        echo "   ${file}"
    fi
done

当您创建文件 20091026.00.${RANDOM}20091028.23.${RANDOM}(含)时,这是几个示例运行:

pax> ./loopscript.sh 20091026.07 9
From 20091026.07 to 20091026.09
   20091026.07.21772
   20091026.08.31390
   20091026.09.9214
pax> ./loopscript.sh 20091027.21 28.02
From 20091027.21 to 20091028.02
   20091027.21.22582
   20091027.22.30063
   20091027.23.29437
   20091028.00.14744
   20091028.01.6827
   20091028.02.10366
pax> ./loopscript.sh 00000000.00 99999999.99 # or just leave off the parameters.
   20091026.00.25772
   20091026.01.25964
   20091026.02.21132
   20091026.03.3116
   20091026.04.6271
   20091026.05.14870
   20091026.06.28826
   : : :
   20091028.17.20089
   20091028.18.13816
   20091028.19.7650
   20091028.20.20927
   20091028.21.13248
   20091028.22.9125
   20091028.23.7870

如您所见,第一个参数的格式必须是正确的yyyymmdd.hh。第二个参数可以更短,因为它继承了第一个参数的开头以使其长度正确。

这只会尝试处理存在(来自ls)且格式正确的文件,不是范围内的每个日期/小时。如果您有稀疏文件(包括范围的开头和结尾),这将更有效,因为它不需要检查文件是否存在。

顺便说一下,如果您有兴趣,这是创建测试文件的命令:

pax> for dt in 20091026 20091027 20091028 ; do
         for tm in 00 01 02 ... you get the idea ... 21 22 23 ; do
             touch $dt.$tm.$RANDOM
         done
     done

请不要逐字输入,然后抱怨它创建了如下文件:

20091026.you.12345
20091028.idea.77

我只修剪了这条线,使其适合代码宽度。 :-)

【讨论】:

  • 哇,我对答案感到震惊。我会回来找你的。
  • 如果有人在没有 to 值的情况下输入 ./loopscript.sh 20091026.07。它将从 20091026.07 运行到 20091023.07 有没有办法将其默认为 .23 ?我在代码的顶部有这个 - 试图将当前日期设置为 st/end 时间 from="$(date +%Y)$(date +%m)$(date +%d)".00 " to="$(date +%Y)$(date +%m)$(date +%d)".23" 如果 [[ ! -z "$2" ]] ;然后 from=$2 fi if [[ ! -z "$3" ]] ;然后 to=$3 fi if [[ ${#from} -ne 11 || ${#to} -gt 11 ]] ;然后使用 exit 1 fi if [[ ${#to} -lt ${#from} ]] ;然后 to=${from:0:$((${#from}-${#to}))}${to} fi
  • 直接对位置参数进行第三次测试,而不是前两个测试:${#1},然后是from=${1:-00000000.00}。创建测试文件内部循环:for tm in 0{0..24}; do touch $dt.${tm: -2}.$RANDOM; done 或没有内部循环:touch $dt.0{0..9}.$RANDOM; touch $dt.{10..19}.$RANDOM; touch $dt.{20..23}.$RANDOM
  • @Chasester:仅供参考,您可以一次性完成date +%Y%m%d
  • @Chasester,如果提供了第一个参数而不是第二个参数,请参阅我的更新,了解如何默认为一天结束。
【解决方案3】:

一种可能的解决方案:将日期转换为“自纪元以来经过的秒数”的标准 Unix 表示并循环,每次迭代将此数字增加 3600(一小时的秒数)。示例:

#!/bin/bash

# Parse your input to date and hour first, so you get:
date_from=20090911
hour_from=10
date_to=20091026
hour_to=01

i=`date --date="$date_from $hour_from:00:00" +%s`
j=`date --date="$date_to $hour_to:00:00" +%s`

while [[ $i < $j ]]; do
  date -d "1970-01-01 $i sec" "+%Y%m%d.%H"
  i=$[ $i +  3600 ]
done

【讨论】:

  • 建议每小时 3600 秒 - 并按要求按小时递增。
  • 由于时区的原因,这段代码会产生几个小时的差异。
猜你喜欢
  • 2015-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多