Bash while循环第二次迭代未用正确变量致重命名异常
问题描述
我尝试将下载目录中的2个文件转移至上传目录并执行重命名操作,但第二个文件被重命名为与第一个文件相同的名称,这不符合预期。
执行的Bash脚本
while [[ "$(ls -A "$dowloadlocation/")" ]]; do sourceFileNameWithPath=$(ls -t $dowloadlocation/* | tail -1) sourceFileName=${sourceFileNameWithPath##*/} log "Source file name: $sourceFileName" destinationFilename_temp=${destinationFilename/\$filename/$sourceFileName} dateTime=$(date +%Y%m%d%H%M) destinationFilename=${destinationFilename_temp/\$dateTime/$dateTime} log "Destination file name: $destinationFilename" if [[ ! "$destinationFilename" =~ $filename ]] && [[ ! "$destinationFilename" =~ $dateTime ]] ; then mv -f --backup=numbered $filename $uploadlocation/$destinationFilename else mv -f --backup=numbered $sourceFileNameWithPath $uploadlocation/$destinationFilename fi done
实际输出
Fri Jun 9 16:26:39 CEST 2023: Destination file name: $filename_$dateTime.csv Fri Jun 9 16:26:39 CEST 2023: Moving downloads from downloadfolder to uploadfolder Fri Jun 9 16:26:39 CEST 2023: Source file name: TEST123 Fri Jun 9 16:26:39 CEST 2023: Destination file name: TEST123_202306091626.csv Fri Jun 9 16:26:39 CEST 2023: Source file name: TEST Fri Jun 9 16:26:39 CEST 2023: Destination file name: TEST123_202306091626.csv
预期输出
Fri Jun 9 16:26:39 CEST 2023: Destination file name: $filename_$dateTime.csv Fri Jun 9 16:26:39 CEST 2023: Moving downloads from downloadfolder to uploadfolder Fri Jun 9 16:26:39 CEST 2023: Source file name: TEST123 Fri Jun 9 16:26:39 CEST 2023: Destination file name: TEST123_202306091626.csv Fri Jun 9 16:26:39 CEST 2023: Source file name: TEST Fri Jun 9 16:26:39 CEST 2023: Destination file name: TEST_202306091626.csv
问题根源
核心问题是文件名模板变量被污染:
第一次循环时,destinationFilename被替换为实际文件名(TEST123_202306091626.csv),已经丢失了原始的$filename占位符。第二次循环执行destinationFilename_temp=${destinationFilename/\$filename/$sourceFileName}时,找不到可替换的$filename字符串,导致直接复用了第一次生成的文件名。
另外脚本存在拼写错误:dowloadlocation应为downloadlocation,需注意变量名一致性。
修复方案
每次循环必须基于原始模板变量生成文件名,不能复用已替换过的变量。同时优化循环逻辑,避免死循环和未引用变量的问题:
# 先定义原始文件名模板(假设你原本的模板存在这里) destinationTemplate="$filename_$dateTime.csv" while [[ "$(ls -A "$downloadlocation/")" ]]; do # 按修改时间取目录中最旧的文件(避免ls解析问题,改用find更可靠) sourceFileNameWithPath=$(find "$downloadlocation" -maxdepth 1 -type f -printf "%T@ %p\n" | sort -n | awk '{print $2}' | tail -1) sourceFileName=${sourceFileNameWithPath##*/} log "Source file name: $sourceFileName" # 基于原始模板生成临时文件名,避免污染模板变量 destinationFilename_temp=${destinationTemplate/\$filename/$sourceFileName} dateTime=$(date +%Y%m%d%H%M) destinationFilename=${destinationFilename_temp/\$dateTime/$dateTime} log "Destination file name: $destinationFilename" # 所有路径变量加双引号,避免特殊字符出错 if [[ ! "$destinationFilename" =~ \$filename ]] && [[ ! "$destinationFilename" =~ \$dateTime ]] ; then mv -f --backup=numbered "$filename" "$uploadlocation/$destinationFilename" else mv -f --backup=numbered "$sourceFileNameWithPath" "$uploadlocation/$destinationFilename" fi # 删除已处理的文件,避免循环重复执行 rm "$sourceFileNameWithPath" done
额外优化建议
- 避免用ls解析文件列表:
ls输出不适合脚本处理,改用find或glob可以避免文件名含空格/特殊字符时的错误。 - 强制变量引用:所有涉及路径、文件名的变量必须用双引号包裹,防止意外分割。
- 循环逻辑简化:原循环依赖
ls -A判断目录是否为空,效率较低,处理完文件后直接删除可避免死循环。
内容的提问来源于stack exchange,提问作者MrAnonymous
相关产品推荐
相关产品推荐

