复制文件名范围为 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;
不过,对于下一个人来说,它开始看起来很复杂且难以维护。
试试这些,在你的情况下替换 dst
、starttime
、endtime
,两者都对我有用 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
.
的值
我有一个 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;
不过,对于下一个人来说,它开始看起来很复杂且难以维护。
试试这些,在你的情况下替换 dst
、starttime
、endtime
,两者都对我有用 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
.