10

I want to catch if a variable is multiline in a case statement in POSIX shell (dash).

I tried this:

q='
'
case "$q" in
    *$'\n'*) echo nl;;
    *) echo NO nl;;
esac

It returns nl in zsh but NO nl in dash.

Thanks.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
aaa
  • 207
  • 1
  • 11

2 Answers2

14

The dash shell does not have C-strings ($'...'). C-strings is an extension to the POSIX standard. You would have to use a literal newline. This is easier (and looks nicer) if you store the newline in a variable:

#!/bin/dash

nl='
'

for string; do

    case $string in
        *"$nl"*)
            printf '"%s" contains newline\n' "$string"
            ;;
        *)
            printf '"%s" does not contain newline\n' "$string"
    esac

done

For each command line argument given to the script, this detects whether it contains a newline or not. The variable used in the case statement ($string) does not need quoting, and the ;; after the last case label is not needed.

Testing (from an interactive zsh shell, which is where the dquote> secondary prompt comes from):

$ dash script.sh "hello world" "hello
dquote> world"
"hello world" does not contain newline
"hello
world" contains newline
Kusalananda
  • 320,670
  • 36
  • 633
  • 936
  • 4
    `$nl` doesn't need quoting in the `case` statement, but it would if it contained wildcards. While `;;` is optional immediately before `esac`, omitting it makes maintenance harder because you have to remember to add it if you add another case, so I recommend always including it. – Gilles 'SO- stop being evil' Oct 31 '19 at 14:46
  • 3
    @Gilles'SO-stopbeingevil' Yes, the `$nl` should be quoted (now fixed), but using or not using `;;` before `esac` is clearly a matter of taste. – Kusalananda Oct 31 '19 at 15:40
  • Another idea for the initializing the `$nl` constant: `nl=$(printf "\nx") && nl=${nl%x}`. That makes it a bit more obvious what's actually in `$nl` (does it include tabs, spaces, etc.?). – jrw32982 Nov 06 '19 at 22:26
2

You can include a literal newline (in quotes) as the pattern, just as you did in assigning to the variable:

q='
'
case "$q" in
    *'
'*) echo nl;;
    *) echo NO nl;;
esac

This makes the formatting ugly (you cannot indent the end quote), but should be fully portable. I tested in bash, zsh, ksh, and dash.

Gordon Davisson
  • 4,360
  • 1
  • 18
  • 17