【问题标题】:use bash to update a files date/time stamp使用 bash 更新文件日期/时间戳
【发布时间】:2022-01-04 06:24:19
【问题描述】:

我想使用此代码 sn-p 使用文件名更新文件日期和时间戳:

示例文件名:

2009.07.04-03.42.01.mov
2019.06.08-01.12.08.mov

我收到以下错误““运行 Shell 脚本”操作遇到错误:“触摸:超出范围或非法时间规范:[[CC]YY]MMDDhhmm[.SS]”

我将如何修改此代码 sn-p?

for if in "$@"
do
 date_Time=$(echo "$if" | awk '{ print substr( $0, 1, length($0)-7 ) }' | sed 's/\.//g' | sed 's/-//')
  touch -t "$date_Time" "$if"
done

更新(2022 年 1 月 5 日):...... 我还希望代码适用于以下文件名格式...

以及没有时间信息的文件名(时间默认为下午 12 点):

2009.07.04.mov
2019.06.08.mov

以及带有描述信息的文件名:

2009.07.04-file-description.mov
2019.06.08-video-file info.mp4
2019.06.08-video-old-codec.avi

【问题讨论】:

  • 你的代码对我有用,虽然有点笨拙。你究竟是如何运行你的脚本的?您需要将要修改的文件的名称作为命令行参数传递,不带前导路径。
  • 你没有在错误发生的时候发布你的变量date_Timeif的内容,所以很难说出了什么问题。我建议您在打开set -x 的情况下运行循环。也许其中一个文件的名称模式与您预期的略有不同。

标签: bash macos shell unix


【解决方案1】:

错误消息表明您传入的文件名与您的示例不匹配。如果在没有文件的情况下调用它,可能会修改您的代码以显示错误消息,如果传递的文件带有目录名称,则删除路径。

顺便说一句,if 是一个关键字,因此您可能不想将其用作变量名,即使它是可能的。

#!/bin/sh

if [ $# == 0 ]; then
    echo "Syntax: $0 files ..." >&2
    exit 1
fi

for f in "$@"
do
    date_Time=$(echo "$f" | awk '{ sub(/.*\//, ""); gsub(/[^0-9]+/, ""); print substr( $0, 1, length($0)-7 ) }')
    touch -t "$date_Time" "$if"
done

还请注意我是如何分解出sed 脚本的; awk 可以做sed 可以做的所有事情,所以我在主脚本中包含了最终的转换。 (顺便说一句,sed 's/[-.]//g' 可以同时完成这两项工作;或者您可以通过一次 sed 调用来完成 sed -e 's/\.//' -e 's/-//'。)

如果您使用 Bash,则可以进一步简化:

#!/bin/bash

if [ $# == 0 ]; then
    echo "Syntax: $0 files ..." >&2
    exit 1
fi

for f in "$@"
do
    base=${f##*/}
    dt=${base//[!0-9]/}
    dt=${dt:0:12}
    case $dt 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])
         touch -t "$dt" "$f";;
      *) echo "$0: $f did not seem to contain a valid date ($dt)" >&2;;
    esac
done

还请注意,如果无法从文件名中准确提取 14 位数字,代码现在会如何发出警告。 parameter expansions 有点笨拙,但比分别在每个文件名上调用 Awk 效率更高(而且 Awk 代码也不是特别优雅或健壮)。

【讨论】:

  • 在 bash 中,当您使用保留名称(例如 if)作为变量名时,哪里会有歧义?即使是if=xxxprintenv if 这样的情况也不会造成伤害。
  • 它工作正常,只是很奇怪。
  • 我想知道您是否可以修改上面的代码,以便它也允许仅使用日期格式的文件名?例如... 2008.10.25.mp4 或 2021.10.25.mov 我正在考虑添加第二个匹配 8 个数字而不是 12 个数字的 case 语句。
  • 这些应该都不难,基本上像你说的那样加上case和touch -t "${dt}0000" "$f";;
  • 我添加了上面的代码,但它并不总是更新创建时间,只是修改时间。 --> for f in "$@" do base=${f##*/} dt=${base//[!0-9]/} dt2=${base//[!0-9]/} dt=${dt:0:12} dt2=${dt2:0:8} 案例 $dt 在 [0-9][0-9][0-9][0-9][0-9][ 0-9][0-9][0-9][0-9][0-9][0-9][0-9]) 触摸 -t "$dt" "$f";; *) echo "$0: $f 似乎没有包含有效日期 ($dt)" >&2;; esac case $dt2 in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) touch -t " ${dt2}0000" "$f";; esac 完成
【解决方案2】:

快速小巧的 bash 功能

setDateFileFromName() {
    local _file _dtime
    for _file ;do
        _dtime="${_file%.*.mov}"
        _dtime="${_dtime##*/}"
        touch -t ${_dtime//[!0-9]/} "$_file"
    done
}

然后

setDateFileFromName /path/to store dir/????.??.??-??.??.??.mov

备注。这适用于您的样本格式的文件名。文件名格式的任何更改都会破坏这一点!

【讨论】:

  • 注意:for file ;dofor file in "$@" ;do 的快捷方式!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
相关资源
最近更新 更多