【问题标题】:Copy files with date/time range in filename复制文件名中包含日期/时间范围的文件
【发布时间】:2021-09-12 16:52:48
【问题描述】:

我有一个 bash 脚本,其中包含以下几行:

for ((iTime=starttime;iTime<=endtime;iTime++))
do
    find . -name "*${iTime}*" -exec cp --parents \{\} ${dst} \;
done

我有一个包含几个文件夹的结构,包括子文件夹和树底部的许多文件。这些文件在文件名中标有日期和时间信息,例如“filename_2021063015300000_suffix”。时间格式为 yyyymmddhhmmss,两位数分别表示 1/10 和 1/100 秒。我有很多文件,这意味着我的方法很慢。这些文件的时间距离为几分钟,因此只应复制几个文件(例如,>10000 个中的每个子文件夹 10 个)。

如何在一个查找和复制命令中找到时间范围内的所有文件并将它们全部获取?也许使用一个查找命令获取要复制的所有文件的列表,然后复制文件路径列表?但是我该怎么做呢?

【问题讨论】:

  • 文件名和后缀是否会根据文件而变化,还是文字“文件名”和“后缀”?另外,您是否提前知道文件名和后缀是否包含数字或下划线?
  • 我想我会将find 输出与开始和结束时间作为变量一起通过管道传输到awk,并在那里过滤它们 - 它会非常快。

标签: linux bash shell find cp


【解决方案1】:

如果您的时间跨度相当有限,只需将可接受的文件名内联到单个 find 命令中即可。

find . \( -false $(for ((iTime=starttime;iTime<=endtime;iTime++)); do printf ' %s' -o -name "*$iTime*"; done) \) -exec cp --parents \{\} ${dst} \;

括号内的初始-false谓词只是为了简化以下谓词,以便它们都可以以-o -name开头。

但是,如果您的时间列表很长,这可能会导致“参数列表太长”错误。也许更稳健的解决方案是将时间分辨率传递给命令。

find . -type f -exec bash -c '
  for f; do
    for ((iTime=starttime;iTime<=endtime;iTime++)); do
      if [[ $f == *"$iTime"* ]]; then
        cp --parents "$f" "$0"
        break
      fi
    done' "$dst" {} +

-exec 中的脚本可能更优雅;如果您的文件名具有合理的常规格式,也许只需提取时间戳并进行数字比较以检查它是否在范围内。或许还注意到我们如何滥用bash -c '...' 之后的$0 参数来传递$dst 的值。

【讨论】:

    【解决方案2】:

    丢失find。我创造了 -

    filename_2020063015300000_suffix 
    filename_2021053015300000_suffix 
    filename_2021063015300000_suffix 
    filename_2022063015300000_suffix
    foo/filename_2021053015312345_suffix
    bar/baz/filename_2021053015310101_suffix
    

    所以如果我执行

    starttime=2021000000000000
    endtime=2022000000000000
    shopt -s globstar
    for f in **/*_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*; do       # for all these
      ts=${f//[^0-9]/}                      # trim to date
      (( ts >= starttime )) || continue     # skip too old
      (( ts <= endtime   )) || continue     # skip too new
      echo "$f"                             # list matches
    done | xargs -I{} echo cp {} /new/dir/  # pass to xargs
    

    我明白了

    cp bar/baz/filename_2021053015310101_suffix /new/dir/
    cp filename_2021053015300000_suffix /new/dir/
    cp filename_2021063015300000_suffix /new/dir/
    cp foo/filename_2021053015312345_suffix /new/dir/
    

    种方法可以简化这个 glob。如果您使用extglob,您可以缩短它,并使用正则表达式更仔细地检查 - 例如,

    shopt -s globstar extglob
    for f in **/*_+([0-9])_*; do
      [[ "$f" =~ _[0-9]{16}_ ]] || continue;
    

    不过,对于下一个人来说,它开始看起来很复杂且难以维护。

    【讨论】:

    • 我相信在原始问题中使用了该查找,因为有问题的文件可能分布在多个目录中。
    • 这个解决方案对我不起作用,所以我去找三人组的答案。
    • 总是追求有效的方法,但你能告诉我它是如何失败的吗?我想更正,所以它可能适用于其他人。
    • 不幸的是,直到现在还没有时间,来自 Tripleee 的答案是唯一一个开箱即用的答案,我理解。您的答案中的问题: ts={$f//[^0-9]/} 显示文件名中的所有数字,因此如果文件名中的其他数字不是日期/时间数字,它将不起作用。我该如何调整这条线,只有两个下划线之间的 16 位数字被分配给 ts?
    • [[ "$f" =~ _[0-9]{16}_ ]](如前所述使用扩展通配符)完全匹配且仅匹配下划线之间有 16 位数字的文件名。如果您不希望扩展 globbing 和单独的匹配检查,您可以使用 f in **/*_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*
    【解决方案3】:

    试试这些,替换你的dststarttimeendtime,在Ubuntu16.04上都对我有用。

    find . -type f -regextype sed -regex "[^_]*_[0-9]\{16\}_[^_]*" -exec bash -c 'dt=$(echo "$0" | grep -oP "\d{16}"); [ "$dt" -gt "$2" ] && [ "$dt" -lt "$3" ] && cp -p "$0" "$1"' {} 'dst/' 'starttime' 'endtime' \;
    

    $0 是包含日期时间的文件名,$1 是 dst 目录路径,$2 是开始时间,$3 是结束时间

    或者

    find . -type f -regextype sed -regex "[^_]*_[0-9]\{16\}_[^_]*" | awk -v dst='/tmp/test_find/' '{if (0 == system("[ $(echo \"" $0 "\"" " | grep -oP \"" "(?<=_)\\d+(?=_)\") -gt starttime ] && [ $(echo \"" $0 "\"" " | grep -oP \"" "(?<=_)\\d+(?=_)\") -lt endtime ]")) {system("cp -p " $0 " " dst)}}'
    

    他们两个,首先,使用find 找到具有_2021063015300000_ 之类模式的文件名(也许这有16 个数字但你说这个模式format yyyymmddhhmmss 只有14 个数字)和sed regex

    然后使用-exec bash -c "get datetime in filename, compare them with times, and exec cp action"

    或者使用awk获取日期时间并通过system命令与开始或结束时间进行比较,最后也会通过system命令执行cpdst目录。

    附言。这种模式依赖于两个_之间只有datetime的文件名。

    【讨论】:

    • 我不会拒绝你的答案,无论如何不幸的是它对我不起作用,所以我会选择三人组的答案
    • 好的,你能给我一些错误提示吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2015-12-11
    • 2012-11-15
    • 2019-04-11
    相关资源
    最近更新 更多