想要一个使用 Bash 脚本在 Linux 环境中管理临时文件的解决方案。
- 如果文件位于根文件夹中并且在 10 天内保持未修改状态,请将文件移至子文件夹并添加时间戳前缀(例如“2022-01-01_文件名”)。
- 如果先前移动的文件被修改,则应将其返回到根文件夹,并且不带时间戳前缀。
- 删除自上次修改日期起 90 天内未修改的文件。
这是我尝试过的。它给了我一些错误,但在我修复这些错误之后,它似乎没有做任何事情。不管怎样,在考虑了几个小时之后,我想我最好使用一个可以按修改日期排序的文件管理器。
#!/bin/bash
FOLDER="$HOME/tmp"
SUBFOLDER="$HOME/tmp/old"
[ ! -d "$FOLDER" ] && mkdir -p "$FOLDER"
[ ! -d "$SUBFOLDER" ] && mkdir -p "$SUBFOLDER"
find "$FOLDER" -maxdepth 1 -type f -mtime +10 -exec sh -c '
for file do
timestamp=$(date +"%Y-%m-%d")
new_filename="$SUBFOLDER/$timestamp_$(basename "$file")"
mv "$file" "$new_filename"
done' sh {} +
find "$SUBFOLDER" -type f -mtime -10 -exec sh -c '
for file do
new_filename="$FOLDER/${file#*_}"
mv "$file" "$new_filename"
done' sh {} +
find "$SUBFOLDER" -type f -mtime +90 -exec rm {} \;
chmod +x /path/to/script.sh
crontab -e
0 0 * * * /path/to/script.sh
答案1
该代码存在几个问题(其中一些问题较小):
- 您不将这些
FOLDER
和变量导出到环境中,因此它们在由 启动的调用SUBFOLDER
中不可用。sh
find
- 目录
~/tmp
应该是私有的,而应该是mkdir -m 700 ~/tmp
. - 这
[ -d ... ] || mkdir ...
是典型的TOCTOU比赛模式。不需要事先检查mkdir -p
。 $(...)
删除尾随换行符,因此不能用于任意文件名- 你不做任何错误处理。您至少可以报告退出状态是否有错误。
- 您不需要在每次迭代时计算今天的日期
- 请注意,这
-mtime +10
适用于 11 天或更早的文件,并且-mtime -10
适用于不到 10 天的文件,因此此处未涵盖 24 小时期限的文件(最后修改时间为 10 到 11 天前的文件)。 - 在第二个命令中,如果存在名称中
find
没有或包含字符的文件,则将无法按预期工作。例如,如果是,则将成为并且对于,则给出。_
$SUBFOLDER
_
$file
/home/you/tmp/old/old-file
$new_file_name
/home/you/tmp//home/you/tmp/old/old-file
/home/your_home/tmp/old/2023-10-01_file
/home/your_home/tmp/home/tmp/old/2023-10-01_file
- 冲突,例如当已存在的目标文件
mv
(甚至可能作为目录或目录的符号链接)未处理时。 -maxdepth
不是标准find
谓词。
和zsh
:
#! /bin/zsh -
autoload zmv || exit
mkdir -pm 700 -- ~/tmp && cd -P -- ~/tmp && mkdir -p old || exit
today=${(%):-%D{%F}} ret=0
# move 10 day old or older regular files
zmv '*(#qND.m+9)' 'old/${today}_$f' || ret=$?
# move back regular files that are less than 10 day old.
zmv 'old/<1900-2100>-<1-12>-<1-31>_(*)(#qND.m-10)' '$1' || ret=$?
# remove regular files that are 91 day old or older
rm -f old/*(ND.m+90) || ret=$?
exit $ret
(sh
使用 bash 没有意义,因为 bash 没有比 sh 更有用的扩展名,这对这里有帮助;您自己的代码中没有任何内容是特定于 bash 的,并且您确实.sh
为脚本提供了扩展名而不是.bash
):
#! /bin/sh -
mkdir -pm 700 -- ~/tmp && cd -P -- ~/tmp && mkdir -p old || exit
TODAY=$(date +%Y-%m-%d) || exit
export TODAY
ret=0
# move 10 day old or older regular files
find . ! -name . -prune -type f -mtime +9 -exec sh -c '
ret=0
for file do
file=${file#./}
mv -i -- "$file" "old/${TODAY}_$file" || ret=$?
done
exit "$ret"' {} + || ret=$?
# move back regular files that are less than 10 day old.
LC_ALL=C find old/. ! -name . -prune \
-name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_*' \
-type f -mtime -10 -exec sh -c '
ret=0
for file do
new=${file#*/./}
new=${new#*_}
if [ -d "$new" ]; then
printf>&2 "%s\n" "$new is a directory"
ret=1
else
mv -i -- "$file" "$new" || ret=$?
fi
done
exit "$ret"' {} + || ret=$?
# remove regular files that are 91 day old or older
find old/ -mtime +90 -type f -exec rm -f {} + || ret=$?
exit "$ret"
-i
这里使用的选项来mv
处理冲突。如果以交互方式运行,当文件将被覆盖时,系统会提示您。如果按cron
stdin is运行/dev/null
,则会拒绝覆盖,但不幸的是不会失败返回。另请参阅-n
GNU 选项mv
(但也不返回失败)。
如果作为目录存在(或目录的符号链接),也mv a b
将移入a
而b
不是将其重命名为。因此要事先进行检查,但这也引入了 TOCTOU 比赛; GNU 实现有一个有帮助的选项。's在其冲突处理中具有类似的竞争条件;传递到GNU 系统至少可以帮助缓解其中的一些问题。b
b
[ -d
mv
-T
zsh
zmv
-o -nT
zmv
(注意:我没有测试过任何代码)。