从文件名中存在的日期字符串设置文件修改时间

Set file modification time from the date string present in the filename

我正在恢复一些名称中包含日期的档案,大致如下:

user-2018.12.20.tar.xz
user-2019.01.10.tar.xz
user-2019.02.25.tar.xz
user-2019.04.19.tar.xz
...

我想通过 xargs 将文件名传送到 touch 并使用 replace-str 设置日期,从而将每个文件的修改日期设置为与文件名中的日期相匹配。

touch -m -t 将采用 [CCYYMMDDhhmm] 格式的日期时间,但我无法替换内联:

find . -name "*.xz" | xargs -I {} touch -m -t $(sed -e 's/\.tar\.xz//g; s/user-//g; s/\.//g; s/\///g; s/$/0000/g' {}) {}

Returnstouch: invalid date format ‘./user-2018.03.22.tar.xz’,尽管这样:

find . -name "*.xz" | sed -e 's/\.tar\.xz//g; s/user-//g; s/\.//g; s/\///g; s/$/0000/g'

Returns 格式正确的日期,例如 201812200000。我是否以某种方式在我的替换字符串中滥用了命令替换?

编辑: 是的,一个简单的脚本就可以做到这一点。但问题仍然存在...

您不需要 findsedxargs 或任何第三方工具,只需使用 shell 内置的正则表达式功能即可获得来自文件的时间戳

 for file in *.tar.xz; do
     [ -f "$file" ] || continue
     if [[ $file =~ ^user-([[:digit:]]+).([[:digit:]]+).([[:digit:]]+).tar.xz$ ]]; then
         dateStr="${BASH_REMATCH[1]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}0000"
         touch -m -t "$dateStr"
     fi
 done

问题是当您调用 xargs 时命令替换将被评估一次,而不是对每个参数。您需要为此生成一个 shell:

find . -name "*.xz" \
  | xargs -I {} bash -c 'touch -m --date "$(sed -e "s/\.tar\.xz//;s/user-//g; s/\.//g; s/\///g;" <<< "")" ""' -- {}

注意:不需要xargs,因为您可以使用find-exec选项:

find . -name "*.xz" -exec bash -c 'touch -m --date "$(sed -e "s/\.tar\.xz//;s/user-//g; s/\.//g; s/\///g;" <<< "")" ""' -- {} \;

PS: 一个小的 for 循环会更具可读性:

for file in user-*.tar.xz ; do
    # remove prefix and suffix
    date=${file#user-}
    date=${date%.tar.xz}
    # replace dots by /
    date=${date//./\/}

    touch -m --date "${date}" "${file}"
done

这可能对你有用(GNU 并行):

parallel --dryrun touch -m --date '{= s/[^0-9]//g =}' {} ::: *.xz

如果命令正确,则删除 --dryrun 选项。

选择:

parallel touch -m --date `{= s/user-//;s/\.tar\.xz//;s/\.//g =}' {} ::: *.xz