为何Bash与Csh循环仅最后一次迭代生效?求解决方法
问题排查与解决:Shell脚本循环仅最后一次迭代生效
问题现象
- 编写Bash/Csh脚本遍历CSV中的字符串,搜索对应文件路径并写入输出文件,但仅最后一次循环生效
- 单独执行
echo $str时内容符合预期,但echo完整find命令时,前几次迭代输出异常(仅显示后半段),只有最后一次能输出完整命令
用户提供的脚本如下:
Bash脚本
#!/bin/bash #CSV file containing the list of strings to search for strings_file="/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv" #Output file where the full path of the file containing the strings will be appended output_file="/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_id_paths.txt" #Read the strings from the CSV file strings=$(cat "$strings_file") #Search the current directory and all subdirectories for files containing the strings and "MRRSU" in their filename for str in $strings; do find . -type f -name "*$str*MRRSU*.IMG" -exec readlink -f {} ; >> "$output_file" done
Csh脚本
#!/bin/csh # CSV file containing the list of strings to search for set strings_file = "/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv" # Output file where the full path of the file containing the strings will be appended set output_file = "/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_id_paths.txt" # Read the strings from the CSV file set strings = `cat $strings_file` # Search the current directory and all subdirectories for files containing the strings and "MRRSU" in their filename foreach str ($strings) find . -type f -name "*$str*MRRSU*.IMG" -exec readlink -f {} \; >> $output_file #set files = `find . -type f -name "*$str*MRRSU*.IMG" -exec readlink -f {} \;` #echo $files >> $output_file end #foreach str ($strings) # set files = `find . -type f -name "*$str*MRRSU*.IMG"` # if ($#files >= 0) then # foreach file ($files) # set full_path = `readlink -f $file` # echo $full_path >> $output_file # end # endif #end
问题根源
你的CSV文件大概率带有Windows风格的换行符(\r\n),而非Unix系统默认的(\n)。当用cat读取文件时,每行末尾的\r(回车符)会被保留在字符串变量中(最后一行可能例外)。
当执行echo find . -type f -name "*$str*MRRSU*.IMG" ...时,$str末尾的\r会强制光标回到行首,后续内容会覆盖掉前面的find . -type f -name "*xxx部分,导致输出看起来不完整。同时,带\r的字符串无法匹配实际文件名(文件名不含回车符),所以前几次循环的find命令找不到文件,只有最后一行无\r的字符串能正常执行。
你可以用cat -A your_file.csv查看文件,若每行末尾有^M,则确认是Windows换行符问题。
解决方法
方法1:预处理CSV文件,转换换行符
直接将文件转换为Unix格式:
# 用dos2unix工具(多数系统自带) dos2unix /project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv # 若无dos2unix,用sed替代 sed -i 's/\r$//' /project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv
方法2:修改脚本,读取时过滤回车符
Bash脚本优化(逐行读取更可靠)
#!/bin/bash strings_file="/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv" output_file="/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_id_paths.txt" # 逐行读取并过滤回车符,跳过空行 while IFS= read -r str; do str=$(echo "$str" | tr -d '\r') [ -z "$str" ] && continue find . -type f -name "*$str*MRRSU*.IMG" -exec readlink -f {} \; >> "$output_file" done < "$strings_file"
Csh脚本优化
#!/bin/csh set strings_file = "/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_ids.csv" set output_file = "/project/crism/users/phillms1/Murchie2017MDAP/tyrrhena_tile_id_paths.txt" # 读取时直接过滤回车符,跳过空行 foreach str (`cat $strings_file | tr -d '\r'`) if ($str == "") continue find . -type f -name "*$str*MRRSU*.IMG" -exec readlink -f {} \; >> $output_file end
额外提示
- 尽量避免用
cat直接赋值给变量后遍历,逐行读取的方式能更好处理空行、特殊字符等边界情况 - 若CSV文件中有空格或特殊字符,建议用更严谨的读取方式(比如Bash中用
IFS=$'\n'限定换行符分隔)
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

