复制文件名范围为 date/time 的文件

Copy files with date/time range in filename

我有一个 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。我创建了 -

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;

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

试试这些,在你的情况下替换 dststarttimeendtime,两者都对我有用 Ubuntu16.04

find . -type f -regextype sed -regex "[^_]*_[0-9]\{16\}_[^_]*" -exec bash -c 'dt=$(echo "[=10=]" | grep -oP "\d{16}"); [ "$dt" -gt "" ] && [ "$dt" -lt "" ] && cp -p "[=10=]" ""' {} '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 \"" [=11=] "\"" " | grep -oP \"" "(?<=_)\d+(?=_)\") -gt starttime ] && [ $(echo \"" [=11=] "\"" " | grep -oP \"" "(?<=_)\d+(?=_)\") -lt endtime ]")) {system("cp -p " [=11=] " " 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命令与开始或结束时间进行比较,最后也会通过cp执行到dst目录system命令。

PS。此模式依赖于两个 _.

之间只有 datetime 的文件名

如果您的时间跨度相当有限,只需将可接受的文件名内联到单个 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" "[=11=]"
        break
      fi
    done' "$dst" {} +

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

的值