Strings
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)