tags:

views:

51

answers:

2
while [ $done = 0  ]
  do
  echo -n "Would you like to create one? [y/n]: "
  read answer
  if [ "$(answer)" == "y" ] || [ "$(answer)" == "Y" ]; then
    mkdir ./fsm_$newVersion/trace
    echo "Created trace folder in build $newVersion"
    $done=1
  elif [ "$(answer)" == "n" ] || [ "$(answer)" == "N" ]; then
    $done=2
  else
    echo "Not a valid answer"
  fi
done

Ok so I have this simple bashscript above that simply just tries to get input from a user and validate it. However I keep getting this error

./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found
./test.sh: line 1: answer: command not found

Which I have no idea why because "answer" is nowhere near line 1. So I ran into this article

Which makes sense since it's referring to line 1 and can't find answer. So it seems to be starting a new subshell. However I didn't really understand the solution and can't see how I would apply it to my case. I just wanna get this to work.

+5  A: 

$(answer) doesn't substitute the value of the variable answer. It executes answer as a command, and substitutes the output of that command. You want ${answer} everywhere you have $(answer). In this case you can get away with bare $answer too, but overuse of ${...} is good paranoia.

(Are you perhaps used to writing Makefiles? $(...) and ${...} are the same in Makefiles, but the shell is different.)

By the way, you have some other bugs:

  • In shell, you do not put a dollar sign on the variable name on the left hand side of an assignment. You need to change $done=1 to just done=1 and similarly for $done=2.
  • You are not being paranoid enough about your variable substitutions. Unless you know for a fact that it does the wrong thing in some specific case, you should always wrap all variable substitutions in double quotes. This affects both the mkdir command and the condition on the while loop.
  • You are not being paranoid enough about arguments to test (aka [). You need to prefix both sides of an equality test with x so that they cannot be misinterpreted as switches.
  • == is not portable shell, use = instead (there is no difference in bash, but many non-bash shells do not support == at all).

Put it all together and this is what your script should look like:

while [ "x${done}" = x0 ]; do
  echo -n "Would you like to create one? [y/n]: "
  read answer
  if [ "x${answer}" = xy ] || [ "x${answer}" = xY ]; then
    mkdir "./fsm_${newVersion}/trace"
    echo "Created trace folder in build $newVersion"
    done=1
  elif [ "x${answer}" = xn ] || [ "x${answer}" = xN ]; then
    done=2
  else
    echo "Not a valid answer"
  fi
done
Zack
Ahh thank you!! I've been battling this for an hour now. This is the first time I've attempted to write a bash script (or any kind of script)
Albinoswordfish
Glad to be of help. I expanded the answer a bit, you had some other bugs.
Zack
Oh, also, the reason it's saying "line 1" is because "answer" is on line one *of the nested shell script induced by `$(...)`*. Yah, that's less than helpful, indeed arguably a bug in the shell.
Zack
I don't really understand what adding the 'x' does.
Albinoswordfish
If you quote your variables inside `[]` you don't need the archaic "x". And regarding portability, if you're writing a `#!/bin/bash` script, you might as well use `[[]]` and `==` (inside `(())` you *must* use `==` for comparison since `=` is used for assignment).
Dennis Williamson
Isn't my original code quoting inside []?
Albinoswordfish
@Albinoswordfish: The 'x' defends against the possibility that the variable's value might begin with a dash, which would be interpreted as an option (so, for instance, without the 'x' if $answer was "-e", it might try to test for the existence of a file named "=", which is not what you wanted). @Dennis: Quoting the variables only defends against them expanding to the wrong number of space-separated arguments; it doesn't help with a leading dash. And I don't believe in writing bash-specific shell scripts; if you can't write your shell script portably, you're better off switching to perl.
Zack
I suppose I should hedge even more, and say that yes, *some* shells will do a string comparison for `[ -e = Y ]`, but it's not something one can rely on.
Zack
A: 

Which I have no idea why because "answer" is nowhere near line 1. So I ran into this article

That's not your problem here.

I ran the script and did not get the error you got. I did receive the error:

./test.sh: line 1: [: -eq: unary operator expected

when I tried to compile though. Defining done fixed this. The following script should work...

#!/bin/bash

done=0
while [ $done -eq 0 ]
do
  echo -n "Would you like to create one? [y/n]: "
  read answer
  if [[ "$(answer)" == "y" || "$(answer)" == "Y" ]]; then
    mkdir ./fsm_${newVersion}/trace
    echo "Created trace folder in build $newVersion"
    $done=1
  elif [[ "$(answer)" == "n" || "$(answer)" == "N" ]]; then
    $done=2
  else
    echo "Not a valid answer"
  fi
done

...note you were doing string comparisons on your done variable, which you apparently intended to be numeric. It's generally bad form to do string comparison on a numeric type variable, though it will work. Use -eq (arithmetic comparison operator) instead. (Also note that if you kept that test, your string equality would be inconsistent... you had "=" in one spot and "==" in another spot... nitpicking here, but it's helpful to be consistent).

Also, I suggest double brackets for your compound conditionals as they will be more readable if you have longer ones. e.g.

if [[($var1 -eq 0 && $var2 -eq 1) || ($var1 -eq 1 && $var2 -eq 0)]]; then

Just a matter of preference as you only have two conditions, but could be useful in the future.

Also you were missing braces '{' '}' around your newVersion variable.

Finally, I'd suggest putting the line #!/bin/bash on the top of your script. Otherwise its up to your environment to determine what to do with your script, which is a bad idea.

Jason R. Mick
Double-bracket tests, sadly, are far less portable than single-bracket tests, which work everywhere as long as you know all of the little tricks (like x-prefixes and not using -a/-o).
Zack
Perhaps, but I think they're much more readable and I haven't run across any major distros that they didn't work on so far that I recall (used Red Hat, CentOS, Ubuntu, and Fedora...)
Jason R. Mick
You really don't encounter much in the way of shell portability headaches if you limit yourself to Linux. Poke at OpenSolaris without any of the GNU tools installed and without `/usr/xpg4/bin` in your $PATH, some time. (And then if your sanity survives that, find yourself an AIX account.)
Zack
Fair enough. Fortunately I haven't had to go there, though some of our old makefiles at my old job had solaris references (I guess at one point we supported it). :)
Jason R. Mick
P.S. Care to answer this question of mine, o bash scripting guru? http://stackoverflow.com/questions/3561326/bash-compound-conditional-with-wildcards-and-file-existence-check
Jason R. Mick