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

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

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

IMG_20171015_133516.jpg
IMG_20171015_133827.jpg
IMG_20171015_142634.jpg
IMG_20171015_142834.jpg
IMG_20171015_142857.jpg

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

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

find -type f -exec touch {} \;

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

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

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

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

回报

touch:无效的日期格式“.”

答案1

在外壳中:

for f in ./IMG_*.jpg ; do 
    t=${f##*/} 
    touch "$f" -t "${t:4:8}${t:13:4}.${t:17:2}"
done

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

这样做find也需要 shell:

$ 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 {} \+

脚本后 Bash 的第一个参数指向变量$0,该变量通常包含 shell 名称。其余的转到位置参数$1$2等和for f/或for 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)

相关内容