我创建了两个脚本。第一个读取所有文件夹的 mtime 时间戳并将其逐行写入 time_old.txt:
for d in */ ; do
time_old=$(stat --format %Y "$d")
escaped=$(printf %q "$d")
echo "$time_old $escaped" >> time_old.txt
done
第二个读取这些行并touch
使用文件夹上的旧时间戳运行:
input="time_old.txt"
while IFS= read -r line
do
echo "$line"
touch_output=$(touch -m -d @$line)
echo "$touch_output"
done < "$input"
这适用于文件夹Folder1
,Folder2
但不适用于名为Foo & Bar
.
我的错在哪里?
相反,做(假设 GNU 工具):
并恢复:
如果您使用
printf %q
,则需要将结果解释为 shell 代码。那可能是:和:
恢复。
但是您需要一个
printf
支持该非标准的独立实用程序%q
,并且它以与 shell 语言兼容的格式引用文件路径。如果不能保证,那可能会变得很危险。当您为每个目录分叉和执行一个进程时,这也会大大降低效率(您的 GNUstat
方法也是如此)。如果可以避免的话,我不会有由 shell 解释的外部输入生成的代码。另请注意,
touch
它不会在标准输出上写任何内容,因此output=$(touch...)
毫无意义。在这里,您还可以使用
zsh
which has a safe quoting operator(使用单引号,无论文件名包含什么字符或非字符文件名都应该是安全的,并且与任何类似 sh 的 shell(除了 yash)兼容)并且具有内置stat
(实际上早于 GNUstat
)所以你不必依赖 GNUisms。并用
sh -v ./time_old.sh
.您已经安排在文件中引用文件名,但问题是引号仅在它们直接在 shell 命令行上时才有效,扩展的结果不会被解析为引号、反斜杠转义或其他 shell 语法. 相反,这些字符是按字面意思处理的。所以如果
$line
是1650819409 Foo\ \&\ Bar
,它将被分词为1650819409
,Foo\
,\&\
, 和Bar
。这类似于我们如何运行存储在变量中的命令中的问题?(您可以使用eval
shell 解析扩展值,但这有其自身的一组问题,尤其是对数据的不可信修改。)如果您对假设文件名不能包含换行符感到满意,则可以将它们原始存储。这将创建类似的行
1650819409 Foo & Bar
,其中第一个空格之后的所有内容都是文件名。