Check if a shell variable has more than one line using built-ins?

5,737

Solution 1

The POSIX way:

NL='
'
case $myvar in
  *"$NL"*) echo more than one line ;;
        *) echo one line ;;
esac

This also works in pre-POSIX Bourne-like shells, too.

Solution 2

The following snippets works in bash (with and without the -posix option):

#!/bin/bash
#!/bin/bash -posix
version_1 () { [[ "$myvar" = *$'\n'* ]]; }
version_2 () {
  local newlines="${myvar//[^$'\n']/}"
  [[ "${#newlines}" -eq 1 ]]
}
for test in version_1 version_2; do
  if $test; then echo many lines; else echo one line; fi
done

Solution 3

Here is a way that should work with all Bourne syntax / POSIX shells and uses only builtins :

if (set -f ; IFS=$'\n'; set -- x${myvar}x ; [ $# = 1 ]) ; then
  echo "Your variable has only one line, proceeding"
else
  echo "Error condition, variable must have exactly one line"
fi

If your shell doesn't support IFS=$'\n' (like dash 0.5.7), you can use instead :

IFS="
"

Solution 4

I'd recommend using Bash parameter expansion as follows:

n="${myvar//[^\n]}"; if [ ${#n} -eq 1 ]; then
  echo "Your variable has only one line, proceeding"
else
  echo "Error condition, variable must have only one line"
fi

Test Samples:

myvar="xxx"
myvar="xx\nxx"
myvar="xx\nx\nx"
Share:
5,737

Related videos on Youtube

Wildcard
Author by

Wildcard

Updated on September 18, 2022

Comments

  • Wildcard
    Wildcard almost 2 years

    I can do this by calling the external utility sed (for a known non-empty $myvar) like so:

    if [ "$(printf %s "$myvar" | sed -n '$=')" -eq 1 ]; then
      echo "Your variable has only one line, proceeding"
    else
      echo "Error condition, variable must have only one line"
    fi
    

    Is there a way to do this using only bash builtins?

    Even better, is there a way to do this in a POSIX-specified manner, without calling external utilities?


    (This question is one of curiosity and finding better ways to do things; the above code does function. I'm wondering if there is a cleaner/faster way.)

    • Admin
      Admin about 8 years
      With that function, the result of myvar="ab" is exactly the same as the result of myvar=$'ab\n'. Should a trailing newline be ignored?. In my opinion, it shouldn't.
    • Admin
      Admin about 8 years
      @BinaryZebra, you are absolutely correct for the general case. In my specific case, I was setting the variable in the first place using command substitution, so was guaranteed to have no trailing newlines.
    • Admin
      Admin about 8 years
      So, if the variable has no trailing "new line" and contains no "new line" it should be counted as "one line". If a trailing "new line" is added, it should be counted as "one more", correct?. There is only one case left that the function with sed fails to process and in my opinion should be counted as "one line" (there are no "new lines" included in the variable). ... ... ... what if myvar='' or unset myvar?
    • Admin
      Admin about 8 years
      @BinaryZebra, this is what I get for failing to assiduously reduce my code to a simplest case standalone example. :) The conditional line itself is taken from my actual production script; the echo lines are obviously filler. Earlier in the script (just after the command substitution) I checked that the variable started with the expected pattern—so I already know the variable is not empty or unset. This is my question post, not an answer, so I don't feel it's necessary to edit it at this point, but feel free to propose one if you see a simple way to make it clearer.
  • Lucas
    Lucas about 8 years
    I think this you need to set IFS otherwise the answer is wrong: sh -c 'a="x y"; set -- $a; echo $#' prints 2 for me, sh -c $'a="x y"; IFS="\n"; set -- $a; echo $#' on the other hand prints 1.
  • jlliagre
    jlliagre about 8 years
    @Lucas Thanks for your comment, answer updated.
  • Stéphane Chazelas
    Stéphane Chazelas about 8 years
    The set -f will apply to future commands. You need set -f; set -- $myvar. Also set -- $myvar` on a myvar=$'\n\nx\n\n\n' would split into 1 element. And myvar='' or myvar=$'\n\n\n' into 0.
  • jlliagre
    jlliagre about 8 years
    @StéphaneChazelas Thanks Stéphane, I originally used two set instructions when tolf I was missing -f but overlook the issue introduced by an edit that came later. I agree empty lines are still an issue with this approach.
  • jlliagre
    jlliagre about 8 years
    @BinaryZebra no problem, the issue was tricky to detect.
  • Admin
    Admin about 8 years
    The 1977 Bourne shell had no functions. I'd expect those kind of redirections wouldn't work great there either. Redirection was quite buggy back then there. It's a bit pointless trying to be portable to such an old shell anyway.
  • Admin
    Admin about 8 years
    Answer updated to fully solve the myvar=$'\n\n\n' problem.
  • Admin
    Admin about 8 years
    IIRC, It didn't support set -- either. It may have supported set - $var, but set - alone would still be the same as set (again from vague memory). There, you'd need set x $var; shift.
  • Admin
    Admin about 8 years
    Testing on a PDP11 emulator running Unix v7, it looks like set - worked there. The need for set x $var; shift might have been for another shell. Note that yash at least doesn't support set - $var.
  • Admin
    Admin about 8 years
    @StéphaneChazelas I fully agre with your words: ` It's a bit pointless trying to be portable to such an old shell anyway.` .... Yes, set -- was not there originally: set -- was not implemented originally, but came with the second release of the Bourne shell.. ... ... Yes set x $var; shift would be the usual workaround. ... ... ... ... Thanks for all your informative comments. Bye.
  • Admin
    Admin about 8 years
    @StéphaneChazelas Where is Unix v7 (for pdp11) available?
  • Admin
    Admin about 8 years
  • jlliagre
    jlliagre about 8 years
    @BinaryZebra with this last edit, an empty myvar becomes an issue...
  • jlliagre
    jlliagre about 8 years
    @BinaryZebra With the last syntax, an empty variable leads to 1 (one line) while I guess the OP expects this situation to fail the test as it would fail with his/her sample script.
  • Admin
    Admin about 8 years
    Ah! good point. I understand you now. But that is an obvious failure of the original test. The correct question that should be answered is: "how many new lines contain the variable". If it is zero, as in myvar=ab, then there is "only one line". How many new lines contains an empty variable?. ... ... ... In any case, if that is really required, a test for an empty $myvar could be added. Which I believe it shouldn't.
  • Admin
    Admin about 8 years
    As is also incorrect that with that function with myvar="ab" is exactly the same as with myvar=$'ab\n'. Should a trailing newline be ignored?.