In bash, how do I count the number of lines in a variable?

BashString

Bash Problem Overview


I have a variable which has a string stored in it and need to check if it has lines in it:

var=`ls "$sdir" | grep "$input"`

pseudo-code:

while [ ! $var's number of lines -eq 1 ]
  do something

That's my idea on how to check it. echo $var | wc -l doesn't work - it always says 1, even though it has 3.

echo -e doesn't work as well.

Bash Solutions


Solution 1 - Bash

Quotes matter.

echo "$var" | wc -l

Solution 2 - Bash

The accepted answer and other answers posted here do not work in case of an empty variable (undefined or empty string).

This works:

echo -n "$VARIABLE" | grep -c '^'

For example:

ZERO=
ONE="just one line"
TWO="first
> second"

echo -n "$ZERO" | grep -c '^'
0
echo -n "$ONE" | grep -c '^'
1
echo -n "$TWO" | grep -c '^'
2

Solution 3 - Bash

Another way using here strings in bash:

wc -l <<< "$var"

As mentioned in this comment, an empty $var will result in 1 line instead of 0 lines because here strings add a newline character in this case (explanation).

Solution 4 - Bash

A simpler version of @Julian's answer, that works for all strings, with or without trailing \n (it does count a file containing just a single trailing \n as empty):

printf "%s" "$a" | grep -c "^"

  • Returns zero: unset variable, empty string, string containing bare newline
  • Returns 1: any non-empty line, with or without trailing newline
  • etc

Output:

# a=
# printf "%s" "$a" | grep -c "^"
0

# a=""
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf "\n")"
# printf "%s" "$a" | grep -c "^"
0

# a="$(printf " \n")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf " ")"
# printf "%s" "$a" | grep -c "^"
1

# a="aaa"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n" "aaa")"
# printf "%s" "$a" | grep -c "^"
1

# a="$(printf "%s\n%s" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

# a="$(printf "%s\n%s\n" "aaa" "bbb")"
# printf "%s" "$a" | grep -c "^"
2

Solution 5 - Bash

No one has mentioned parameter expansion, so here are a couple of ways using pure bash.

Method 1

Remove non-newline characters, then get string length + 1. Quotes are important.

 var="${var//[!$'\n']/}"
 echo $((${#var} + 1))

Method 2

Convert to array, then get array length. For this to work, don't use quotes.

 set -f # disable glob (wildcard) expansion
 IFS=$'\n' # let's make sure we split on newline chars
 var=(${var})
 echo ${#var[@]}

Solution 6 - Bash

You can substitute the "wc -l" with "wc -w" to rather count the number of words instead of lines. This will not count any new lines and can be used to test if your original results are empty before you continue.

Solution 7 - Bash

The top voted answers fail if no results were returned by a grep.

Homer Simpson
Marge Simpson
Bart Simpson
Lisa Simpson
Ned Flanders
Rod Flanders
Todd Flanders
Moe Szyslak

This is the wrong way to do it:

wiggums=$(grep -iF "Wiggum" characters.txt);
num_wiggums=$(echo "$wiggums" | wc -l);
echo "There are ${num_wiggums} here!";

There will tell us, there is 1 Wiggum in the list, even if there aren't any.

Instead, you need to do one extra check to see if the variable is empty (-z, as in "is zero"). If grep didn't return anything, the variable will be empty.

matches=$(grep -iF "VanHouten" characters.txt);

if [ -z "$matches" ]; then
    num_matches=0;
else
    num_matches=$(echo "$matches" | wc -l);
fi

echo "There are ${num_matches} VanHoutens on the list";

Solution 8 - Bash

Another method to count number of lines in a variable - assuming you did check it was successfully filled or it is not empty, for that just check $? after var subshell result affectation - :

readarray -t tab <<<"${var}"
echo ${#tab[@]}

readarray|mapfile is bash internal command which converts input file, or here string in this case, to array based on newlines.

-t flag prevents storing newlines at end of array's cells, useful for later use of stored values

Advantages of this method are :

  • no external command (wc, grep, ...)
  • no subshell (pipe)
  • no IFS issues (restore after modification, tricky to use with command-limited scope on internal commands, ...)

Solution 9 - Bash

To avoid filename in "wc -l" command:

lines=$(< "$filename" wc -l)
echo "$lines"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionkrack krackerzView Question on Stackoverflow
Solution 1 - BashIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 2 - BashJulianView Answer on Stackoverflow
Solution 3 - BashspeakrView Answer on Stackoverflow
Solution 4 - BashStilezView Answer on Stackoverflow
Solution 5 - BashhaqkView Answer on Stackoverflow
Solution 6 - BashChristopher BrunsdonView Answer on Stackoverflow
Solution 7 - BashJonathan MatthewsView Answer on Stackoverflow
Solution 8 - BashadrenochromeView Answer on Stackoverflow
Solution 9 - Bashuser3503711View Answer on Stackoverflow