我正在尝试将在同一文件夹中找到的每个access.log.gz
文件(注意:确切的文件名,而不是“以...开头”或“包含”) 。/home/*/logs
date-access.log.gz
for file in /home/*/logs/access.log.gz
do
mv -v "$file" /home/*/logs/old/`date +\%G-\%m-\%d`"-access.log.gz"
done
我当然得到了/home/*/logs/2014-09-08-access.log.gz - No such file or directory
。我该如何调用*
路径?每个文件都应重命名并移动到源文件夹的 /old/ 子目录。我尝试过
mv -v "$file" /home/`$1`/logs/old/`date +\%G-\%m-\%d`"-access.log.gz"
...但没有作用。
答案1
使用find
(如何更有效地使用 find 命令?)。
find /home/ -ipath */logs/access.log.gz
这样应该可以找到您感兴趣的所有文件并打印它们。您可以将其插入到脚本中,但让其 find
完成所有艰苦的工作可能更容易。
find /home/ -ipath */logs/access.log.gz -execdir mv "{}" "old/`date +\%G-\%m-\%d`-access.log.gz" \;
find
我们在这里使用的功能是:
-ipath
-name
与找到的文件名中的模式匹配。如果您确定每个子路径中只有一个 access.log.gz 文件,也可以使用(-name access.log.gz
)。-execdir
从包含匹配文件的子目录中对每个找到的文件执行给定的命令。这相当于对cd
每个文件所在的位置执行操作,然后在那里运行命令。在这种情况下,它节省了大量的路径操作逻辑。找到的文件将替换 {}。还要注意,“old”是作为相对路径给出的(正如您所说的,它old/
存在于 中logs/
)
答案2
$file
在迭代匹配的文件时,只需简单地剖析并重新组装:${file%/*}
为您提供包含最后一次/
删除之后的所有内容的 $file,并${file##*/}
为您提供包含最后一次/
删除之前的所有内容的 $file;结合起来${file%/*}/old/$date-${file##*/}
会为您提供对应于的“新”文件名$file
。
假设在运行脚本的过程中日期不会改变,因此没有必要date
对每个文件都调用它;相反,将日期存储在一个变量中一次,然后在循环期间使用它:date=$(date '+%G-%m-%d')
。(请注意,现代脚本应该使用$(...)
而不是反引号。还要注意,这%
不是一个“特殊”字符,因此不需要引用或转义,但作为一种良好做法,整个格式字符串都应该被引用。)
如果您有新版本的 Bash,则可以通过使用来完全避免使用子 shell printf -v date '%(%G-%m-%d)T' -1
。
您可能还会发现它很有用,shopt -s globstar
这样您就可以在目录中搜索任意深度,并且shopt -s nullglob
不匹配的 glob(通配符)将扩展为“无”而不是原始的 glob 模式。
综合以上,我们得到:
shopt -s globstar nullglob
date=$( date '+%G-%m-%d' )
for file in /home/*/logs/**/access.log.gz
do
target=${file%/*}/old/$date-${file##*/}
mv -vi "$file" "$target"
done