简体   繁体   中英

Shell not evaluating multiple conditions in IF statement

I want to sort files into folders based on two conditions, but it won't evaluate both conditions. The conditions are based on values read in from a text file (index.txt) that correspond to the particular subject, and values are coded as 0 or 1

Subid   DX    SEX
XXXX    0      0
YYYY    1      0

the code is as follows

    #!/bin/sh 
    d=XXXX
    dx=$(awk '{if($1=='$d'){print $2}}' index.txt)
    sex=$(awk '{if($1=='$d'){print $3}}' index.txt)
    if [ "$dx" == "0" ] && [ "$sex" == "0" ];then
     commands
    fi

I have tried using double brackets [[ <condition 1> ]] && [[ <condition 2> ]]; , collapsing brackets if [ <condition1> && <condition2> ]; , parentheses, and using -eq instead of == , but they still are not evaluating and no errors are being printed. What am I missing?

One of the most useful tricks when debugging a shell script is set -x (or running the entire script with sh -x ). This makes the shell print the equivalent of each command as it executes them, so you can get a better idea what's actually happening and whether it matches what you expect. Let's try it on your script:

$ sh -x reader.sh
+ d=XXXX
++ awk '{if($1==XXXX){print $2}}' index.txt
+ dx=
++ awk '{if($1==XXXX){print $3}}' index.txt
+ sex=
+ '[' '' == 0 ']'

Note that it set both dx and sex to the empty string. And if you look at the awk script that's being executed, you can see why: XXXX doesn't have double-quotes around it, so awk is treating it as a variable name, rather than a literal string to match against.

You could add double-quotes, but that's not really the best way to do it; sticking literal strings into executable code (an awk script in this case) risks parts of the strings being mistaken for executable code, which can have really weird/bad consequences. The better way to do this sort of thing is to pass the data around as data , by using awk's -v option to set a variable, and then using that. Something like:

$ awk -v d="XXXX" '{if($1==d){print $2}}' index.txt
0

Hey, it printed the right thing! So here's a version of your script using that:

#!/bin/sh
d=XXXX
dx=$(awk -v d="$d" '{if($1==d){print $2}}' index.txt)
sex=$(awk -v d="$d" '{if($1==d){print $3}}' index.txt)
if [ "$dx" = "0" ] && [ "$sex" = "0" ];then
    commands
fi

Note that the awk variable named d has no intrinsic connection to the shell variable named d ; the -vd="$d" bit copies the shell variable into the same-named awk variable. Also, I switched the == to just = in the comparison, since that's the standard string-equal operator inside [ ] . bash allows == as a synonym, but not all shells do this; if you're writing specifically for bash, you should use a specific bash shebang line ( #!/bin/bash or #!/usr/bin/env bash ) rather than #!/bin/sh .

(Actually, I'd recommend using a bash shebang, because if you're not clear on what's standard shell syntax and what's a bash extension, you probably aren't writing portably anyway, so explicitly requesting bash is safer. And if you're doing that, you might as well switch to [[ ]] instead of [ ] , because it avoids many of the syntactic oddities that [ ] expressions suffer from.)

The problem is not with your conditions but with your variables set with awk.

Try the following to set your variables:

#!/bin/sh
d=XXXX
dx=$(awk '{if ($1 ~ /'$d'/) print $3}' index.txt)
sex=$(awk '{if ($1 ~ /'$d'/) print $3}' index.txt)
if [ "$dx" = "0" ] && [ "$sex" = "0" ];then
echo "works"
fi

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM