Shell脚本含空变量的Else-If语句失效问题排查
问题分析与解决方案
哦,我一眼就看出问题出在哪了——你把三个文件的判断条件混在一起用||(或)逻辑,导致只要有一个文件满足“存在第三个变量”的条件,整个脚本就直接跳进第一个分支,完全不管另外两个文件的实际状态!
核心问题拆解
拿你的示例数据来说:file3的ANC35是SAS(非空),所以第一个if的条件:
if [ "${ANC3}" != "" ] || [ "${ANC31}" != "" ] || [ "${ANC35}" != "" ]; then
会直接成立,脚本立刻执行三文件合并的操作。但这时候file1和file2的ANC3/ANC31是空的,你去合并它们的三个vcf文件肯定会报错,而且第二个、第三个分支根本没机会运行——因为第一个条件已经满足了,elif和else都不会被触发。
你的需求是每个文件独立根据自己的变量状态执行对应操作,但原逻辑把三个文件的条件绑在一起做整体判断,这完全不符合实际场景(比如可能file1是两变量、file2是一变量、file3是三变量,原逻辑根本处理不了这种情况)。
修正后的解决方案
正确的思路是:为每个文件单独编写判断逻辑,或者用一个函数封装重复的处理逻辑,然后分别调用处理三个文件。这样每个文件的状态互不影响,都能执行对应的操作。
步骤1:封装处理单个文件的函数
把重复的合并/复制逻辑写成函数,避免冗余代码:
# 定义处理单个文件的函数 # 参数:1. 文件前缀(如fileref1) 2. ANC1变量 3. ANC2变量 4. ANC3变量 process_file() { local file_prefix=$1 local anc1=$2 local anc2=$3 local anc3=$4 # 判断第三个变量是否非空 if [[ -n "$anc3" ]]; then echo "***** 处理 $file_prefix:三变量合并 *****" bcftools merge -m both \ "${file_prefix}.genotypes_${anc1}.vcf.gz" \ "${file_prefix}.genotypes_${anc2}.vcf.gz" \ "${file_prefix}.genotypes_${anc3}.vcf.gz" \ -Oz \ -o "${file_prefix}.new.genotypes_${anc1}.${anc2}.${anc3}.vcf.gz" # 判断第二个变量非空、第三个为空 elif [[ -n "$anc2" ]]; then echo "***** 处理 $file_prefix:两变量合并 *****" bcftools merge -m both \ "${file_prefix}.genotypes_${anc1}.vcf.gz" \ "${file_prefix}.genotypes_${anc2}.vcf.gz" \ -Oz \ -o "${file_prefix}.new.genotypes_${anc1}.${anc2}.vcf.gz" # 第二、三个变量都为空 else echo "***** 处理 $file_prefix:单变量复制 *****" cp "${file_prefix}.genotypes_${anc1}.vcf.gz" "${file_prefix}.new.genotypes_${anc1}.vcf.gz" fi }
步骤2:调用函数处理三个文件
在提取变量后,分别调用函数处理每个文件:
# 提取变量的逻辑保持不变 ANC1=$(sed -n 1p file1 | cut -f 1 -d' ' ) ANC2=$(sed -n 2p file1 | cut -f 1 -d' ' ) ANC3=$(sed -n 3p file1 | cut -f 1 -d' ' ) ANC11=$(sed -n 1p file2 | cut -f 1 -d' ' ) ANC21=$(sed -n 2p file2 | cut -f 1 -d' ' ) ANC31=$(sed -n 3p file2 | cut -f 1 -d' ' ) ANC15=$(sed -n 1p file3 | cut -f 1 -d' ' ) ANC25=$(sed -n 2p file3 | cut -f 1 -d' ' ) ANC35=$(sed -n 3p file3 | cut -f 1 -d' ' ) # 分别处理三个文件 process_file "fileref1" "$ANC1" "$ANC2" "$ANC3" process_file "fileref2" "$ANC11" "$ANC21" "$ANC31" process_file "fileref3" "$ANC15" "$ANC25" "$ANC35"
额外优化建议
- 用
[[ ]]替代[ ]:[[ ]]是Bash的扩展语法,支持更安全的字符串判断,不需要担心变量为空时的语法错误(比如[ "$var" != "" ]如果var为空会变成[ != "" ],而[[ -n "$var" ]]不会有这个问题)。 - 用
-n/-z判断空值:-n表示非空,-z表示空,比!= ""/== ""更符合Shell脚本的规范写法。 - 变量提取逻辑优化:可以用循环简化变量提取,比如:
这样可以减少重复代码,尤其是当文件数量或变量数量增加时更方便。# 提取file1的三个变量 for i in 1 2 3; do eval "ANC$i=\$(sed -n ${i}p file1 | cut -f 1 -d' ' )" done # 提取file2的三个变量 for i in 1 2 3; do eval "ANC${i}1=\$(sed -n ${i}p file2 | cut -f 1 -d' ' )" done # 提取file3的三个变量 for i in 1 2 3; do eval "ANC${i}5=\$(sed -n ${i}p file3 | cut -f 1 -d' ' )" done
内容的提问来源于stack exchange,提问作者joeblow
相关产品推荐
相关产品推荐

