Else-If Satement с пустыми переменными не работает

Я пытаюсь написать условие if-else в сценарии оболочки/bash, которое будет использоваться для многих разных файлов, поэтому оно не будет соответствовать определенной структуре.

У меня есть три разных файла и до трех разных переменных, выбранных из каждого из этих файлов, которые входят в оператор if-else. В моем сценарии я написал это (что, вероятно, можно было бы написать лучше) в начале следующим образом:

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' ' )

Например, из этих файлов могли получиться следующие переменные:

echo ${ANC1}
FIN
echo ${ANC2}
NFE
echo ${ANC3}


echo ${ANC11}
FIN
echo ${ANC21}
NFE
echo ${ANC31}


echo ${ANC15}
FIN
echo ${ANC25}
NFE
echo ${ANC35}
SAS 

Отсюда я написал оператор if-else (с учетом возможных отсутствующих переменных в трех файлах, как указано выше). Чтобы понять это, попытайтесь сделать следующее:

первое условие: если все переменные не пусты; второе условие: если третья переменная является единственной отсутствующей переменной; третье условие: если третья и вторая переменные пусты

if [ "${ANC3}" != "" ] || [ "${ANC31}" != "" ] || [ "${ANC35}" != "" ]; then

    echo "***** three variables *****"

    bcftools merge -m both \
    fileref1.genotypes_${ANC1}.vcf.gz \
    fileref1.genotypes_${ANC2}.vcf.gz \
    fileref1.genotypes_${ANC3}.vcf.gz \
    -Oz \
    -o fileref1.new.genotypes_${ANC1}.${ANC2}.${ANC3}.vcf.gz

    bcftools merge -m both \
    fileref2.genotypes_${ANC11}.vcf.gz \
    fileref2.genotypes_${ANC21}.vcf.gz \
    fileref2.genotypes_${ANC31}.vcf.gz \
    -Oz \
    -o fileref2.new.genotypes_${ANC11}.${ANC21}.${ANC31}.vcf.gz

    bcftools merge -m both \
    fileref3.genotypes_${ANC15}.vcf.gz \
    fileref3.genotypes_${ANC25}.vcf.gz \
    fileref3.genotypes_${ANC35}.vcf.gz \
    -Oz \
    -o fileref1.new.genotypes_${ANC15}.${ANC25}.${ANC35}.vcf.gz

elif 
    [ "${ANC3}" == "" -a "${ANC2}" != "" ] || [ "${ANC31}" == "" -a "${ANC21}" != "" ] || [ "${ANC35}" == "" -a "${ANC25}" != "" ]; then

    echo "***** two variables *****"

    bcftools merge -m both \
    fileref1.genotypes_${ANC1}.vcf.gz \
    fileref1.genotypes_${ANC2}.vcf.gz \
    -Oz \
    -o fileref1.new.genotypes_${ANC1}.${ANC2}.vcf.gz

    bcftools merge -m both \
    fileref2.genotypes_${ANC11}.vcf.gz \
    fileref2.genotypes_${ANC21}.vcf.gz \
    -Oz \
    -o fileref2.new.genotypes_${ANC11}.${ANC21}.vcf.gz

    bcftools merge -m both \
    fileref3.genotypes_${ANC15}.vcf.gz \
    fileref3.genotypes_${ANC25}.vcf.gz \
    -Oz \
    -o fileref1.new.genotypes_${ANC15}.${ANC25}.vcf.gz

elif 
    [ "${ANC3}" == "" -a "${ANC2}" == "" ] || [ "${ANC31}" == "" -a "${ANC21}" == "" ] || [ "${ANC35}" == "" -a "${ANC25}" == "" ]; then 

    echo "***** one variable ***** "

    cp fileref1.genotypes_${ANC1}.vcf.gz fileref1.new.genotypes_${ANC1}.${ANC2}.vcf.gz

    cp fileref2.genotypes_${ANC11}.vcf.gz fileref2.new.genotypes_${ANC11}.vcf.gz

    cp fileref3.genotypes_${ANC15}.vcf.gz fileref1.new.genotypes_${ANC15}.vcf.gz

fi

Каждый раз, когда я запускаю этот скрипт, должно создаваться 3 файла, но иногда это не так. Первая часть работает (для файлов, где все переменные не пусты), но второе и третье условия, похоже, не работают. Я также пробовал использовать [ -z "${ANC3}" ] и [ -n "${ANC2}" ] для обозначения пропавших без вести и не пропавших без вести соответственно, но это тоже не сработало. Также пробовал [[ ]], сравнивал [ ], но все равно.

Что-то, что я явно упускаю?


person joeblow    schedule 12.05.2018    source источник
comment
Являются ли переменные действительно пустыми или они содержат пробельные символы?   -  person Kusalananda    schedule 12.05.2018
comment
У нас почти наверняка есть дубликат вашего вопроса, но он настолько спрятан внутри кода, что не имеет ничего общего с операторами if и пустыми переменными, что почти невозможно сказать. Попробуйте следовать правилам создания минимально воспроизводимого примера -- самого короткого кода кто-то другой может побежать, чтобы увидеть проблему самостоятельно, с четким разделением фактических и предполагаемых результатов — для вопросов, связанных с кодом.   -  person Charles Duffy    schedule 12.05.2018
comment
Кстати, есть гораздо, гораздо более эффективные способы чтения полей из ваших первых трех строк в переменные, чем набор sed вызовов. { read anc1 _; read anc2 _; read anc3 _; } <file1 считывает первый столбец первых трех строк в три отдельные переменные оболочки за один проход без участия подоболочек или внешних команд.   -  person Charles Duffy    schedule 12.05.2018
comment
Вам не нужно sed здесь. Например, { read ANC1 _; read ANC2 _; read ANC3 _; } < file1.   -  person chepner    schedule 12.05.2018
comment
Кстати, обратите внимание, что == в качестве тестового оператора является нестандартным расширением (стандартный оператор сравнения строк — =), и использование -a или -o для объединения нескольких тестов помечено как устаревшее (см. маркеры [OB XSI] в pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html). И имена, написанные заглавными буквами, используются переменными, значимыми для оболочки, тогда как имена, содержащие хотя бы один символ нижнего регистра, гарантированно не повлияют на работу оболочки; см. pubs.opengroup.org/onlinepubs/9699919799/basedefs/, четвертый абзац.   -  person Charles Duffy    schedule 12.05.2018
comment
... Итак, если в вашем вопросе всего две строки кода, одна из которых устанавливает для ваших переменных определенное значение, при котором ваш if не работает так, как вы ожидаете, а вторая строка с этим if и спрашивает, почему if разветвляется в одну сторону, а не в другую... это было бы намного легче следовать (и любой мог бы запустить его самостоятельно, даже в онлайн-интерпретаторе, без необходимости создавать file1 и file2 и так далее). Кроме того, размещение присваиваний в вопросе с явными литералами исключает такие неожиданности, как поля файла, которые выглядят пустыми, но содержат непечатаемые символы.   -  person Charles Duffy    schedule 12.05.2018


Ответы (2)


Я не уверен, что понимаю, как должна работать логика, но я думаю, что у вас могут возникнуть проблемы с Законы де Моргана, которые связаны с тем, как логическое отрицание сочетается с И и ИЛИ. Английский имеет тенденцию быть довольно неряшливым в этом отношении, поэтому вам нужно тщательно подумать, когда вы переводите то, что хотите, в логику кода. В частности, вы сказали «первое условие: если все переменные не пусты», но соответствующий оператор if:

if [ "${ANC3}" != "" ] || [ "${ANC31}" != "" ] || [ "${ANC35}" != "" ]; then

..фактически соответствует «если ЛЮБАЯ из переменных не пуста».

В приведенном вами примере ANC3 и ANC31 оба пусты (поэтому первые два теста возвращаются как ложные), а ANC35 не пусто (это «SAS»), поэтому третий тест верен. false || false || true оценивается как true, так что условие if в целом истинно, и эта ветвь оператора if будет выполнена. Это то, что должно произойти, если только одна из переменных не пуста?

Если я прав насчет проблемы, то первое выражение if должно иметь &&s вместо ||s, например:

if [ "${ANC3}" != "" ] && [ "${ANC31}" != "" ] && [ "${ANC35}" != "" ]; then

Также могут быть похожие проблемы с тестами elif, но, как я уже сказал, я не уверен, что понимаю, что он должен делать правильно.

person Gordon Davisson    schedule 12.05.2018
comment
Технически вы правы, и это то, что у меня было в первый раз. Однако причина, по которой я изменил его на || с &&, заключается в том, что он применяется ко всем трем файлам (file1, file2, file3) как and/or. То есть, например, для первого условия это не if all three variables are not empty in all three files then do stuff. Это if all three variables are not empty in any of the 3 files, then do stuff in the files where the three variables are not empty. Это логика и в следующих двух условиях. Имеет ли это смысл? Извиняюсь за то, что сделал это запутанным. - person joeblow; 13.05.2018

Это не полный ответ, но вот простые примеры оболочки POSIX с заданными переменными $x, $y и $z:

первое условие: если все переменные не пусты;

[ "$x" -a "$y" -a "$z" ] && do_stuff

второе условие: если третья переменная является единственной отсутствующей переменной;

[ "$x" -a "$y" -a ! "$z" ] && do_stuff

третье условие: если третья и вторая переменные пусты

[ "$y$z" ] || do_stuff
person agc    schedule 13.05.2018