1

I have a script, that reads some input foo, and based on that input tries to calculate the value for the variable bar. If it is successful, then it shall set bar. If not, the user should be able to input that value for bar manually.

Currently I have:

read foo
bar=$(some manipulation with foo)
if [ $bar ]
then 
  echo $bar
else
  read bar
fi

The problem I'm experiencing is that in some cases I get more than a single word answer back from the manipulation of foo. I know I could work around that with quotes or the new test, but that's not what I want. I'd like that if some manipulation with foo returns multiline output, that it asks to set bar manually. The same reaction as to no answer. How would I do that?

3 Answers3

1

You are looking for the array size of bar:

#!/bin/bash

read foo
bar=(${foo})

case ${#bar[@]} in
  0) echo "bar has 0 words" ;;
  1) echo "bar has 1 word" ;;
  *) echo "bar has > 1 words" ;;
esac

Depending of the input you get the information how many words bar contains. The ${#bar[@]} gives the number of elements bar contains.

  • thanks, that's almost it. so what I needed to do is replace if [ $bar ] with if [ ${#bar[@]} == 1 ]. Awesome. – user857990 Feb 04 '14 at 09:30
  • 1
    Beware that if foo is '*' for instance, $bar will contain the names of all the files in the current directory. – Stéphane Chazelas Feb 04 '14 at 09:40
  • And, I think this is not working for multiline texts. This is working for a single line with many words; is this the requested behaviour? – eppesuig Feb 04 '14 at 09:44
  • @StephaneChazelas is set -f;read foo;set +f the common way to prevent globbing? –  Feb 04 '14 at 09:50
  • @bersch, no you need the set -f in effect in bar=($foo), that is when you use the split+glob operator (leaving a variable expansion unquoted) and only want the split part. read doesn't do globbing. – Stéphane Chazelas Feb 04 '14 at 09:52
  • @eppesuig, bar=($foo) splits based on the current value of $IFS. The default value of $IFS has space, tab and newline. So it will give you all the words of every line. – Stéphane Chazelas Feb 04 '14 at 09:55
  • @StephaneChazelas right, so you will have a counter of words, while I think the question is about a counter of lines. – eppesuig Feb 04 '14 at 09:57
  • @eppesuig, yes, the question is unclear on that front. Note that the OP used the split+glob operator in his question when leaving the $bar unquoted in [ $bar ] (which I agree does not make sense) – Stéphane Chazelas Feb 04 '14 at 10:16
1

With any Bourne-like shell:

case $bar in
  "" | *[!$IFS]*[$IFS]*[!$IFS]*) echo empty or multi-word;;
  *) echo OK
esac

Or:

set -f
set -- $bar
if [ "$#" -eq 1 ]; then
   echo OK
else
   echo empty of multi-word, words being:
   printf '  "%s"\n' "$@"
fi

Set IFS to the word separator. For instance, if words are meant to be non-empty lines:

IFS='
'
0

You may just have a small function that check for that. This is an example, with a few tests:

empty_or_multiline()
{
  [ -z "$1" ] && return 0
  count=$(echo "$1" | wc -l)
  [ $count -gt 1 ] && return 0

  return 1
}

# test number 1
bar=
if empty_or_multiline "$bar"
then
  echo Empty or multiline
else
  echo bar="$bar"
fi

# test number 2
bar=jajhsflgkh
if empty_or_multiline "$bar"
then
  echo Empty or multiline
else
  echo bar="$bar"
fi

# test number 3
bar="kjh\nskjfjhgf"
if empty_or_multiline "$bar"
then
  echo Empty or multiline
else
  echo bar="$bar"
fi
eppesuig
  • 3,318