1

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?

jrob
  • 13
  • 4
  • 2
    Where are `$x` and `$y` set? As you show it, they're going to be the empty string. – Benjamin W. Dec 04 '19 at 21:17
  • 2
    Use `=`, not `==`, with `[ ... ]`. – chepner Dec 04 '19 at 21:19
  • Note that the shell *is* evaluating the first condition; it's just finding that `""` (because `x` is undefined) does not equal `"0"`, and so `&&` prevents the second condition from being evaluated. – chepner Dec 04 '19 at 21:21
  • edited to show the variables. Unfortunately using a single `=` didn't do it – jrob Dec 04 '19 at 21:27
  • 2
    Running your code with the file you provided (index.txt), your $dx and $sex variables are empty – Chargaff Dec 04 '19 at 21:29

2 Answers2

2

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 -v d="$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.)

Gordon Davisson
  • 118,432
  • 16
  • 123
  • 151
  • It was reading in my awk syntax fine but I ran sh -x and saw that windows added those invisible carriages to the lines in index and thats why. Thanks alot! – jrob Dec 05 '19 at 14:03
0

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
Chargaff
  • 1,562
  • 2
  • 19
  • 41
  • 1
    Using `==` instead of `=` limits the implements of `/bin/sh` that this code will work with. [The POSIX `test` specification](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html) only defines `=`, and implementations that do not support `==` (always report an error and return false when it's used) are not uncommon. See [`[`: Unexpected operator in shell programming](https://stackoverflow.com/questions/3411048). – Charles Duffy Dec 05 '19 at 00:18
  • (To clarify what I mean by "not uncommon" -- think "installed out-of-the-box on Ubuntu"). – Charles Duffy Dec 05 '19 at 00:22
  • Thank you @CharlesDuffy, corrected my answer to be POSIX compliant. – Chargaff Dec 05 '19 at 12:42