我是 bash 脚本编写的新手,尽管它仍然是初稿,但使该脚本按我的预期工作:
#!/bin/bash
find /var/www/site.com/ -type f -name "*.log" | xargs bash -c '
for name; do
parent=${name%/*}
parent=${parent##*/}
destdir=$(dirname $name)
current_year=$(date +"%Y")
if [[ "$name" =~ _([0-9]{4})- ]] &&
[[ "$name" != *"$current_year"* ]]; then
year="${BASH_REMATCH[1]}"
else continue
fi
if [ ! -d "$destdir/$year/$parent" ]; then
mkdir -p "$destdir/$year/"
fi
mv "$name" "$destdir/$year"
done '
find /var/www/site.com/ -type d -name "20*" | xargs bash -c '
for dir; do
tar_name=$(echo $dir | grep -Eo '[0-9]{4}')
tar cvfj '$tar_name'.tbz $dir
done '
exit
如您所见,我在 bash 脚本中调用了 bash 两次。这是一个不好的做法吗?没有/没什么意义?或者我最好使用 find 命令的输出结果创建一个数组并迭代它?任何建议将不胜感激。提前致谢。 PS 抱歉缺少缩进
答案1
即使不查看内部 shell 脚本,也需要注意一些事项:
如果文件名包含空格、引号或反斜杠字符, Plainxargs
将失败。它有自己的一套引用规则,与其他大多数规则不兼容。(我现在找不到参考问题。)
您缺少内联 shell 脚本的“zeroth”参数,这将导致第一个文件名被跳过。
此外,语法突出显示表明您的第二个命令已损坏,单引号关闭grep -Eo '[0-9]{4}'
并重新打开整个脚本周围的引号。
现在,这个问题xargs
很容易解决,find ... -print0 | xargs -0 command...
许多实现(GNU、FreeBSD、Busybox)都支持这个问题。
要解决第零个参数的问题,您需要添加一个虚拟参数(以 结尾$0
),例如
... | xargs -0 bash -c '...' sh
或者,您也可以使用find ... -exec bash -c '...' sh {} +
.
但最好避免启动另一个 shell。在 Bash 中,你可以这样做:
find ... -print0 | while IFS= read -r -d '' file; do
something with "$file"
done
或者,
while IFS= read -r -d '' file; do
something with "$file"
done < <(find ... -print0)
即使您需要在循环内设置变量并期望它们在循环外可用,后者也可以工作。
请参阅相关部分为什么循环查找的输出是不好的做法?(您没有使用命令替换,因此并非所有内容都是相关的。)
要将 的输出放入find
数组中,请使用
mapfile -t -d '' array < <(find ... -print0)
答案2
假设您的 bash ≥4.3,您可以使用递归通配符而不是调用find
.调用find
并没有错(如果你做得对:见下文),但是当递归通配符起作用时,它就不那么方便了。你需要打电话shopt -s globstar
启用递归通配符。
shopt -s globstar
for name in /var/www/site.com/**/*.log; do
…
done
for dir in /var/www/site.com/**/20*/; do
…
done
如果您确实使用find
,切勿通过管道find
输入xargs
:它们使用不同的语法来分隔文件名,因此如果名称包含空格或\'"
.使用-exec
调用 shell 或使用find … -print0 | xargs -0
.看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?了解更多信息。