9

I have a multiline variable, and I only want the first line in that variable. The following script demonstrates the issue:

#!/bin/bash

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

echo "  Take the first line and send to standard output:"
echo ${STRINGTEST%%$'\n'*}
#   Output is as follows:
# Onlygetthefirstline

echo "  Set the value of the variable to the first line of the variable:"
STRINGTEST=${STRINGTEST%%$'\n'*}

echo "  Send the modified variable to standard output:"
echo $STRINGTEST
#   Output is as follows:
# Onlygetthefirstline butnotthesecond orthethird

Question: Why does ${STRINGTEST%%$'\n'*} return the first line when placed after an echo command, but replace newlines with spaces when placed after assignment?

migrated from serverfault.com May 22 '15 at 10:28

This question came from our site for system and network administrators.

  • 1
    Cannot reproduce it. It works for me as expected. – Peque May 22 '15 at 10:57
  • 1
    Can't reproduce with any of 2.05b, 3.1, 3.2, 4.0, 4.1, 4.2, 4.3 either. Sounds like a user error like trying to run it with a shell that doesn't support $'...' instead of bash. – Stéphane Chazelas May 22 '15 at 11:12
7

Maybe there is other way to archive what you want to do, but this works

#!/bin/bash

STRINGTEST="
Onlygetthefirstline
butnotthesecond
orthethird
"

STRINGTEST=(${STRINGTEST[@]})
echo "${STRINGTEST[0]}"
  • That assumes the lines in $STRINGTEST don't contain blanks or wildcards. Also note that empty lines (as in the first line in that variable) are ignored. – Stéphane Chazelas May 22 '15 at 11:10
  • 3
    Also note that using STRINGTEST=(${STRINGTEST[@]}) makes little sense and is equivalent to STRINGTEST=($STRINGTEST) since STRINGTEST was previously defined as a scalar (not array) variable. – Stéphane Chazelas May 22 '15 at 13:15
8

might not be most efficient but one liner...

firstLine=`echo "${multiLineVariable}" | head -1`
  • 2
    I like it for clarity and brevity. – Peter A. Schneider Aug 29 '17 at 9:36
  • This firstLine=`echo "${test_var}" | sed -n 1p also works if you have a reason to use sed instead (e.g., it means you can simultaneously perform a replacement to the line: echo "${test_var}" | sed -nE '1 s/# *(.*)/\1/p'. – robenkleene May 28 at 17:36
6

That code works for me with all versions of bash I tried between 2.05b and 4.3. More likely you tried to run that script with a different shell that doesn't support the $'...' form of quoting.

That $'...' syntax is not standard sh syntax (yet) and only supported (as of 2015-05-22 and AFAIK) by ksh93 (where it originated), zsh, bash, recent versions of mksh and the sh or recent versions of FreeBSD.

My bet would be that you tried to run that script with sh instead of bash and your sh is based on versions of ash, pdksh, yash or ksh88 that don't support it yet.

If you want to make that code POSIX 2008 compatible, you'd need to write it:

STRINGTEST="Onlygetthefirstline
butnotthesecond
orthethird"

NL='
'
STRINGTEST=${STRINGTEST%%"$NL"*}
printf '%s\n' "$STRINGTEST"

Then, you can have it interpreted by any POSIX compliant shell like bash or any leaner/faster ones like your sh.

(and remember that leaving a variable unquoted in list context has a very special meaning in Bourne-like shells).

  • Or it could be an older version of bash. I could reproduce the behavior with 2.03. – Gilles May 22 '15 at 21:43
  • @Gilles, the last supported OS that included bash-2.03 (released 16 years ago) was probably Solaris 8 which went EOL over 3 years ago. That hypothesis seems pretty unlikely. – Stéphane Chazelas May 22 '15 at 23:00
1

This works for me:

STRINGTEST="Some Text 1
Some Text 2
Some Text 3"

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"

And it also works for blank lines:

STRINGTEST="
Some Text 1
Some Text 2
Some Text 3"

readarray -t lines < <(echo "$STRINGTEST")
echo "${lines[0]}"
  • If one bothers to fire up process substitution one could as well simply read once into a simple variable (instead of readarray plus indexing). – Peter A. Schneider Aug 29 '17 at 9:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.