如何从文件名中的日期字符串设置文件修改时间
问题分析与解决方案
你遇到的核心问题确实是命令替换的执行时机不对——当你写$(sed -e ... {})的时候,这个命令替换是由调用xargs的父shell先执行的,而不是xargs每次处理一个文件时才执行。这时候{}还只是一个占位符字符串,sed处理的是字面量{},而不是实际的文件名,所以生成的"日期"完全不符合要求,touch自然会报错。
下面给你几个可行的命令行解决方案,不用写单独的脚本就能解决:
方案1:用xargs + bash子shell处理每个文件
把sed的处理逻辑放到bash子shell里,让每个文件名都能被单独解析:
find . -name "*.xz" | xargs -I {} bash -c ' DATE=$(sed -e "s/\.tar\.xz//g; s/user-//g; s/\.//g; s/\///g; s/$/0000/g" <<< "$1") touch -m -t "$DATE" "$1" ' _ {}
- 这里
bash -c启动一个子shell,$1代表传入的文件名(也就是{}) <<< "$1"把文件名作为输入传给sed,确保每个文件都能被正确处理_是bash -c脚本的第一个参数(对应$0,我们用不到,所以填个占位符)
方案2:用awk简化日期提取(更简洁)
比起多次sed替换,用awk直接分割文件名提取日期部分更高效:
find . -name "*.xz" | xargs -I {} bash -c ' DATE=$(echo "$1" | awk -F "[-.]" "{print \$2\$3\$4\"0000\"}") touch -m -t "$DATE" "$1" ' _ {}
-F "[-.]"用-和.作为分隔符,文件名./user-2018.12.20.tar.xz会被分成./user、2018、12、20、tar、xz这几部分- 直接拼接
$2$3$4得到20181220,再加上0000就符合touch -t要求的格式了
方案3:用find的-exec替代xargs(更安全)
如果文件名里有空格或者特殊字符,xargs可能会出错,用find -exec更可靠:
find . -name "*.xz" -exec bash -c ' DATE=$(sed -e "s/\.tar\.xz//g; s/user-//g; s/\.//g; s/\///g; s/$/0000/g" <<< "$1") touch -m -t "$DATE" "$1" ' _ {} \;
或者awk版本:
find . -name "*.xz" -exec bash -c ' DATE=$(echo "$1" | awk -F "[-.]" "{print \$2\$3\$4\"0000\"}") touch -m -t "$DATE" "$1" ' _ {} \;
为什么原命令会失败?
再明确一下:原命令中$(sed ... {})是在xargs开始执行前就被父shell解析了。这时候{}还没被替换成实际的文件名,sed处理的是字符串{},输出的结果肯定不是你想要的日期格式,touch收到错误的日期参数,加上后面正确的文件名,就会抛出invalid date format的错误。
内容的提问来源于stack exchange,提问作者tobiasfried
相关产品推荐
相关产品推荐

