About

Everything is Unix systems is either a string (text stream) or binary data (like an image).

In Bash, there is no integer or boolean. Just strings. See also Arrays and Associative arrays cheatsheets.

Also if you want a special character to be a plain string, you must quote it. e.g. Using > vs '>'.

$ echo >
zsh: parse error near `\n'
$ echo '>'
>

Quoting strings

Single quotes

Use single quotes for a literal string that is not evaluated.

X='Hello, $(whoami) / $USER'

echo "$X"
Hello, Hello, $(whoami) / $USER'

Double equotes

Use double quotes to evaluate.

X="Hello, $(whoami) / $USER!"

echo "$X"
Hello, mcurrin / mcurrin!

No quotes

The expression will be evaluated.

$ echo Hello
Hello
$ echo Hello $USER / $(whoami)
Hello mcurrin / mcurrin

If the value is multiple words (separated by space or newline), then the value will be spread out.

When to quote

Quotes are usually optional.

Sometimes they do make a difference.

Empty values

Such as if a variable is empty.

This will cause a syntax error if FOO is not set or empty.

[ -z $FOO ]

This will be safe.

[ -z "$FOO" ]

Words

If a variable multiple words, with a space or newlinws, but you want a single string.

Important for a file path with spaces.

OK:

$ cd "My Projects"

Error:

$ cd My Projects

The same goes for passing arguments using a variable.

$ TARGET="My Projects"
$ # OK
$ cd "$TARGET"

$ # Error because it sees `My` and `Projects` as two arguments.
$ cd $TARGET

Or a URL that has spaces in it. You can also use %20 in place of spaces.

OK:

$ curl "https://example.com/My Projects"
$ curl "https://example.com/My%20Projects"

Error - this would pass two arguments.

$ curl https://example.com/My Projects

Expanding paths

If you using a glob like * or the user directory like ~, you probably want those outside of any quotes to avoid literal evaluate.

OK:

$ cd ~/Documents

Will give an error.

$ cd "~/Documents"

If you need quotes, use $HOME.

$ cd "$HOME/Documents"

Multi-line strings

Using enter

Just press enter for a newline, in single or double quotes.

Nothing special is needed like in other languages to include multiple lines.

X='Hello,
world'

echo "$X"
Hello,
world

Using escaped newline character

You can also use \n for a newline. But you need to use -e flag to evaulate newlines (need in Bash but ZSH doesn’t need the flag).

X="A\nB\nC"
echo -e "$X"
A
B
C

Or using echo directly:

echo "ABC\nDEF\nGHI"
ABC\nDEF\nGHI

With -e flag.

echo -e "ABC\nDEF\nGHI"
ABC
DEF
GHI

Using printf:

printf 'ABC\nDEF\nGHI\n'
ABC
DEF
GHI

Or print:

print 'ABC\nDEF\nGHI'
ABC
DEF
GHI

Wrap a long string without splitting over newlines

Use a backslash to go to the next line.

Make sure to use double quotes or not quotes.

$ echo "abcdef\
123456"
abcdef123456

Or

$ echo abcdef\
123456
abcdef123456

Single quotes will have a literal backslash:

$ echo 'abcdef\
> 123456'
abcdef\
123456

Skip first line

For indentation reason, you might want to leave text off of the first line. Use the \ symbol to split your command so you start the string on the next line.

$ echo \
'First line
Second line'
First line
second line

Escaping quotes

If you want to include quotation marks in your output.

echo "With 'single' quotes"
echo 'With "double" quotes'

echo 'With \'escaped single\' quotes'
echo "With \"escaped single\" quotes"

If you need to use single and double quotes in the same string and don’t want to use \ to escape them, then read on for heredoc below.

Defaults

Set a default value in case a variable is not set.

The syntax here is within ${} substitution, as ${VARIABLE:-DEFAULT}.

e.g.

No default supplied - result is empty string.

$ echo "Hello, ${NAME}"
Hello,

With default/fallback set from string literal.

$ echo "Hello, ${NAME:-World}"
Hello, World
$ NAME=developer echo "Hello, ${NAME:-World}"
Hello, developer

With default/fallback set from a variable.

$ DEFAULT_NAME='World'
$ echo "Hello, ${NAME:-$DEFAULT_NAME}"
Hello, World
$ NAME=developer echo "Hello, ${NAME:-$DEFAULT_NAME}"
Hello, developer

Heredoc strings

See guide.

Bash supports a heredoc:

  • This lets you create a string which implicitly escapes all single and double quotes, letting you write a clean string.
  • Typically, a heredoc is used for writing multi-line strings, but you don’t have it.
  • You might use it for a literal string or evaluate expressions.

For most cases, I don’t need a heredoc. A standard single or double-quoted string works well - including for multiple lines.

But, the heredoc does have the advantage that you can use single and double quotes inside the string without escaping them, as your string terminator will be EOF for example.

Syntax

Note you can use anything, but EOF is the common term to start and end the shell heredoc.

The last occurence should be on a line of its own, without indentation and without characters after it.

In one situation, I found EOF) as the last line was actually valid in my shell, but in a CI flow I got a linting error.

Evaluated string heredoc

cat << EOF
CONTENT
EOF

Example:

cat << EOF
Line 1
Line 2
You are $(whoami)
EOF

Output:

Line 1
Line 2
You are mcurrin

Works with cat but not echo.

You can use anything at the start and end, but EOF for “end of file” is the convention.

A heredoc allows a multi-line string with evaluation. Without having to explicitly escape quotes as "\"user\": \"$(whoami)\"".

Storing as a variable and formatting as a JSON string.

MY_VAR=$(cat << EOF
{
    "user": "$USER"
}
EOF
)

echo "Quoted"
echo "$MY_VAR"
echo "Unquoted"
echo $MY_VAR
Quoted
{
    "user": "mcurrin"
}
Unquoted
{ "user": "mcurrin" }

Note double quotes on echo to keep newlines.

Example - note escaped backticks to prevent executation.

cat << EOF > README.md
## My heading

\`\`\`
$ git clone $REMOTE_URL --branch mybranch && ./$REPO_NAME/script
\`\`\`
EOF

Literal heredoc

Prevent evaluation by using quotes (single or double).

cat << "EOF"
CONTENT
EOF

Example:

cat << "EOF"
Line 1
Line 2
You are $USER
EOF

Output:

Line 1
Line 2
You are $USE

Here, we store a value as a JSON string.

MY_VAR=$(cat << EOF
{
  "user": "$USER"
}
EOF
)

Printing quoted:

$ echo "$MY_VAR"
{
  "user": "my-name"
}

Printing unquoted.

$ echo $MY_VAR
{ "user": "my-name" }

Indent

From guide

The heredoc becomes more useful than a plain string when you use remove common indentation.

Here we indent the text with tab (not spaces) and then use <<- to remove tabs (does not remove spaces.

if true; then
	cat <<- EOF
	Line 1
	Line 2
	EOF
fi

Output:

Line 1
Line 2

Note that EOF must not be indented.

Or, with a variable.

VAR=$(cat <<- EOF
	{
		"user": "$(whoami)",
	}
EOF
)

echo "$VAR"

Piping

cat << 'EOF' | sed 's/l/e/g'
Hello
world
EOF

Output:

Heeeo
wored

Write to file.

cat << 'EOF' |  sed 's/l/e/g' > file.txt
Hello
world
EOF

Heredoc in other languages

PHP

Using a heredoc in PHP - from the PHP manual.

$foo = 'abc;
$x = <<<EOT
Line 1
Line 2
'Single quotes' line.
"Double quotes" line.
$foo
EOT;
}
?>

JavaScript

In JavaScript, you would do use backticks. This will evaulate expressions though.

  • index.js for JavaScript.
      foo = 'abc';
      var x = `\
      Line 1
      Line 2
      'Single quotes' line.
      "Double quotes" line.
      ${foo}
      `
    

Note use of escaped newline at the start, to prevent first line from appearing as empty.

Python

In Python you would use three quotes. You could use double quotes (") usually, just for convention. You would use an f string as below for evaluation.

  • app.py for Python.
      x = f"""\
      Line 1
      Line 2
      'Single quotes' line.
      "Double quotes" line.
      {foo}
      """
    

Note use of escaped newline at the start, to prevent first line from appearing as empty.

Case

From article.

For Bash 4 and up only. Not Bash 3 or ZSH.

Titlecase

$ x='abc'

$ echo "${x^}
Abc

Uppercase

$ x='abc'

$ echo "${x^^}
ABC

For Bash 3 - from answer.

$ MY_VALUE="Some string"
$ printf '%s\n' "$MY_VALUE" | awk '{ print toupper($0) }'
SOME STRING

Selective uppercase

Here we uppercase only p and j letters.

$ LANGUAGES='python perl java php c#'
$ echo ${LANGUAGES^^[p,j]}

Handle inputs

Apply titlecase to an input. e.g. yes becomes Yes.

read -p "Do you like music? " ans
answer=${ans^}
echo "Your answer is $answer."

Remove newlines

Basic:

$ echo 'a
b
c'
# a
# b
# c

Use tr to remove newlines:

echo 'a
b
c' | tr -d '\n'
# abc

Or xargs.

echo 'a
b
c' | xargs
# a b c

You could use either approach when passing values to another command, like where you want to take output over multiple lines from command_b below and send it as arguments to command_a.

```sh command_a $(command_b | xargs)