我正在尝试修改Sergiy 的剧本以递归方式执行(目前它只删除 1 个文件,但我希望它删除所有文件,直到达到最大文件限制)。但我搞不懂。我觉得我应该能够修改第一个函数(“删除最旧的”)以读取多个时间戳并将其传递给下一行,但我不熟悉这个命令,找不到任何相关信息。任何想法都将不胜感激!
答案1
如果希望脚本仍然只对一个目录进行操作,而不对子目录进行递归,则可以循环进行计数和删除while
。函数的最后一部分main
应改为如下所示:
local file_inodes=$(get_files)
while filecount_above_limit
do
printf "@@@ File count in %s is above %d." "$directory" $max_files
printf "Will delete oldest\n"
sort -k1 -n <<< "$file_inodes" | delete_oldest
local file_inodes=$(get_files)
done
printf "@@@ File count in %s is below %d." "$directory" $max_files
printf "Exiting normally"
警告!
这个简单改动的问题在于,如果你没有在顶部的删除行中添加注释,脚本将无限循环,因为它会在每次删除后重新计算文件数量。如果没有删除文件,文件数量将保持不变,循环永远不会退出。
可以以更复杂的方式修改脚本,file_inodes
一旦删除文件 inode,就从数组中删除它,并负向增加file_count
变量,而不是重复该local file_inodes=$(get_files)
行。这将处理非删除检查的情况,但我将把这个留给别人去做。
答案2
我建议使用另一种解决方案,即在目标目录树结构中递归遍历并删除所有文件,但保留预定义的一定数量的新文件。该解决方案基于:(1)递归 bash 脚本以及(2)解释 shell 脚本以递归方式打印完整目录树。
1.创建可执行脚本文件,名为walkr
(走走),位于/usr/local/bin
可作为 shell 命令访问 (更详细的步骤)。
2.脚本的内容walkr
非常简单:
#!/bin/bash
[[ -z "${NFK}" ]] && NFK='7' || NFK="$NFK"
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"
file_operations() {
local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
}
walk() {
cd "$1" && file_operations # Change directory to the destination path and call the above function
for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done # Make the recursion
}
walk "${ABS_PATH}"
3.解释:
一开始,脚本会检查变量
$NFK
(确定要保留的文件数量)是否已预先设置 - 条件[[ -z "${NFK}" ]]
。如果没有设置,则默认值为7
。接下来,脚本处理目标路径(命令的标准输入)。如果没有提供目标路径(条件
[[ -z "${1}" ]]
),脚本将进入当前目录。walk()
最后将执行主要函数。
功能
walk()
:首先,它会将目录更改为目标路径
cd "$1"
,然后调用file_operations()
内部运行的函数。此外,对于
$item
当前目录"$1"/*
(也是目录)中的每个,[[ -d "$item" ]]
该函数walk()
将再次执行,因此我们创建了递归。
功能
file_operations()
:最初它将设置内部 Bash 变量
$IFS
,因此我们可以正确处理<the list of the files to be removed>
,无论单独的文件名里面是否有空格。随后将执行命令
rm $(<the list of the files to be removed>)
。错误重定向2>/dev/null
适用于没有内容可删除的情况。<the list of the files to be removed>
方法如下:该命令
ls -lt
将以长列表格式列出当前目录的内容-l
,并按修改时间排序,最新内容排在第一位-t
。并且此列表通过管道传输|
到下一个命令。下一个命令
grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*'
将裁剪这些^
以-
†开头的行,从开头到模式[0-9]{2}:[0-9]{2}_
‡-P
。带有选项的选项-o
将输出与模式匹配的字符串^-.*[0-9]{2}:[0-9]{2}_
。\K
通知将忽略其之前的匹配部分。(来源-这个有用的答案)†因此,我们将仅从列表中获取文件的名称。在输出中,
ls -l
描述目录的行以 开头d
,而描述文件的行以 开头-
。(想法的来源)‡此模式与时间格式匹配
00:00
。最后命令
tail -n +"$((NFK+1))
将切断前几行我们的文件列表。这些文件的数量前几行等于$NFK
加1的值,这是命令的要求tail
。
4.使用示例:
walkr
针对当前目录运行:walkr # You shouldn't use any argument, walkr ./ # but you can use also this format
walkr
对任何子目录运行:walkr <directory name> walkr ./<directory name> walkr <directory name>/<sub directory>
walkr
要对任何其他目录运行:walkr /full/path/to/<directory name>
要更改要保留的文件数量(例如
3
),请使用此格式NFK=3 walkr NFK=3 walkr /full/path/to/<directory name> # etc.
5.让我们来玩一下脚本walkr
:
我们可以使用命令
touch file.name -d "1 hour ago"
创建一个空文件,日期为一小时前。因此,我们可以使用以下命令来创建如此处所示的目录结构。mkdir -p ~/temp/dir{A..C} && cd ~/temp ;\ DEST=''; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\ DEST='dirA/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\ DEST='dirB/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\ DEST='dirC/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago"
现在我们可以进行一些测试:
更新脚本的功能。以下是上述脚本的更新版本:
#!/bin/bash
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "$1" && ABS_PATH="${PWD}"
[[ -z "${2}" ]] && NFK='7' || NFK="$2" # Number of the files to be kept
[[ -z "${3}" ]] && REC='1' || REC="$3" # REC='1' - work recursively
[[ -z "${4}" ]] && VRB='1' || VRB="$4" # VRB='1' - work in verbose mode
file_operations() {
local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
if [ "$VRB" == "1" ]
then # Verbose mode:
rm -v $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null && printf " -from: '%s' \n" "$1" || echo "nothing to remove in: '$1'"
else # Quiet mode:
rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
fi
}
walk() {
# Change directory to the destination path and call the above function, pass $1 for the verbose mode
cd "$1" && file_operations "$1"
# If REC='1': Recursive mode -- Make the recursion; otherwise work on the curent level
if [ "$REC" == "1" ]; then for item in "$1"/*; do [[ -d "$item" ]] && walk "$item"; done; fi
}
walk "${ABS_PATH}"
此版本的脚本可以处理更多输入变量。它具有安静的和冗长模式并且可以非递归地工作。
完整格式为:
walkr '<destination path>' '<number of lines to be kept>' '<no recursion>' '<quiet>'
<no recursion>
和的确切内容<quiet>
无关紧要。只要输入变量$3
和$4
不能为空,就会覆盖默认行为。