问题重现
在Unix/Linux环境下使用Korn Shell(ksh)脚本处理文件时,经常会遇到需要按日期后缀批量移动文件的需求。比如以下典型场景:
for file in $fileDirectory/*.$yesterdayDate
do
mv *.$yesterdayDate "$fileDirectory/$yesterdayDate"
done
错误分析
执行时出现的错误提示表明脚本无法找到匹配的文件:
mv: cannot stat ‘*.20200607’: No such file or directory
主要原因在于循环体内重复使用通配符*.$yesterdayDate
,而Shell在执行前会先展开通配符。当目录中没有匹配文件时,通配符会保持原样传递给mv命令。
正确实现方式
以下是几种可靠的实现方案:
方案1:使用find命令
find "$fileDirectory" -maxdepth 1 -name "*.$yesterdayDate" \\
-exec mv {} "$fileDirectory/$yesterdayDate/" \\;
方案2:优化后的ksh循环
mkdir -p "$fileDirectory/$yesterdayDate"
for file in "$fileDirectory"/*."$yesterdayDate"; do
[ -e "$file" ] || continue
mv "$file" "$fileDirectory/$yesterdayDate/"
done
方案3:使用nullglob选项
set -o nullglob
files=("$fileDirectory"/*."$yesterdayDate")
(( ${#files[@]} )) && mv "${files[@]}" "$fileDirectory/$yesterdayDate/"
最佳实践建议
- 始终用双引号包裹变量引用
- 先检查目标目录是否存在
- 处理前验证文件是否存在
- 考虑添加错误处理逻辑
完整示例脚本
#!/bin/ksh
fileDirectory="/path/to/files"
yesterdayDate=$(date +%Y%m%d -d "yesterday")
# 创建目标目录
mkdir -p "$fileDirectory/$yesterdayDate" || {
echo "无法创建目录" >&2
exit 1
}
# 移动文件
set -o nullglob
files=("$fileDirectory"/*."$yesterdayDate")
if (( ${#files[@]} )); then
mv "${files[@]}" "$fileDirectory/$yesterdayDate/"
echo "成功移动 ${#files[@]} 个文件"
else
echo "未找到匹配文件"
fi