从存储为文件名的日期设置文件修改日期

mx0*_*mx0 1 find awk date files touch

我有很多文件,其中文件名包含文件创建日期 ( IMG_RRRRMMDD_hhmmss.jpg):

IMG_20171015_133516.jpg
IMG_20171015_133827.jpg
IMG_20171015_142634.jpg
IMG_20171015_142834.jpg
IMG_20171015_142857.jpg
Run Code Online (Sandbox Code Playgroud)

但实际文件创建日期不同。我需要将它从文件名设置回日期。

我知道如何将所有文件的日期更改为当前日期:

find -type f -exec touch {} \;
Run Code Online (Sandbox Code Playgroud)

以及如何解析文件名以获取正确格式的日期 touch -t

echo IMG_20171015_133516.jpg | awk -F_ '{print $2 substr($3,0,4) "." substr($3,5,2)}'
201710151335.16
Run Code Online (Sandbox Code Playgroud)

但我不知道如何将这些命令组合在一起来更改所有文件。

find -type f -exec touch -t"$(echo '{}' | awk -F_ '{print $2 substr($3,0,4) "." substr($3,5,2)}')" {} \;
Run Code Online (Sandbox Code Playgroud)

返回

触摸:无效的日期格式“.”

ilk*_*chu 6

在外壳中:

for f in ./IMG_*.jpg ; do 
    t=${f##*/} 
    touch "$f" -t "${t:4:8}${t:13:4}.${t:17:2}"
done
Run Code Online (Sandbox Code Playgroud)

Bash、ksh、zsh 等都有${var:n:m}子字符串扩展,如果需要递归扩展,请启用globstar并使用**/IMG_*.jpg

这样做也find需要外壳:

$ find -name "IMG_*.jpg" -exec bash -c '
  for f; do t=${f##*/}; touch "$f" -t "${t:4:8}${t:13:4}.${t:17:2}" ; done
  ' sh {} \+
Run Code Online (Sandbox Code Playgroud)

脚本后 Bash 的第一个参数转到$0变量,该变量通常包含 shell 名称。其余转到位置参数$1$2等,for ffor f in "$@"循环他们。这里的构造相当常见(至少在 unix.SE 答案中)。此处循环的主要部分与第一个示例中的相同。


find你的问题中的命令find .. -exec touch -t"$(...)" {} \;不起作用,因为该命令替换为双引号,所以它得到扩大之前find运行。该{}是字面上的管道awk,它只是恰巧,该awk脚本只打印一个点。

如果您确实将命令替换放在引号中,它将按touch原样传递给(find只会替换{})。touch会得到参数-t$(echo ...),美元符号,括号等等。find不会自己通过 shell 运行命令,我们需要明确地执行它,如上所述。

在单引号示例中:

$ find -name x -exec echo 'hello $(foo {})' \;
hello $(foo ./x)
Run Code Online (Sandbox Code Playgroud)