Print Text Reliably with GNU echo
The command echo looks too simple to break, right up until a script prints "-n" and nothing appears. This guide covers the newline, the escapes, the builtin-versus-binary trap and when to reach for printf, in about ten minutes.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need a POSIX-like shell and GNU coreutils. The examples use coreutils 9.4, package version 9.4-3ubuntu6.3. Nothing here needs elevated privileges, and nothing changes on disk or in system configuration.
1. Check which echo you are running
Your shell probably has its own echo that shadows the GNU one. Start by checking how the name resolves in your current shell:
$ type -a echo
echo is a shell builtin
echo is /usr/bin/echo
echo is /bin/echo
A shell builtin normally takes precedence over /usr/bin/echo. Builtins differ between shells, especially around options and backslash escapes. If a script needs the GNU behaviour documented here, call the external command explicitly:
$ command -v /usr/bin/echo
/usr/bin/echo
$ dpkg-query -W coreutils
coreutils 9.4-3ubuntu6.3
The package version is host-specific. Run the check on the machine where the script will run rather than copying this version into a compatibility assumption.
Checkpoint
Use plain echo for an interactive shell experiment, or use /usr/bin/echo when the GNU implementation itself is part of the test.
2. Print ordinary text with the default newline
GNU echo writes all its arguments to standard output, separated by single spaces, then adds a trailing newline. Quote a phrase when you want the shell to pass it as one argument:
$ /usr/bin/echo 'backup complete'
backup complete
$ /usr/bin/echo 'two words' 'and three'
two words and three
Without quotes, the shell performs word splitting before echo receives the text. That usually still gives you spaces between words, but it can also expand wildcards or collapse repeated whitespace. Quoting is a shell rule, not an echo option, and it is the simplest way to keep data together.
Variables need the same care. Double quotes expand a variable and preserve its internal whitespace; single quotes print the dollar sign literally:
$ message='report ready at 09:30'
$ /usr/bin/echo "$message"
report ready at 09:30
$ /usr/bin/echo '$message'
$message
Warning
Do not put untrusted data into an unquoted command line. An unquoted value can undergo pathname expansion, and data beginning with an option can be mistaken for a request to change echo's behaviour.
3. Suppress the trailing newline
Use -n when the output must end immediately after the last character:
$ /usr/bin/echo -n 'waiting'
waiting$ /usr/bin/printf '\n'
The prompt lands on the same line because echo did not terminate its output. That suits small interactive displays, but it makes logs hard to read, and the next command's output shares the line unless that command writes a newline first.
Check the bytes when the final newline matters:
$ /usr/bin/echo -n 'waiting' | od -An -t x1
77 61 69 74 69 6e 67
Those hexadecimal bytes are the letters in waiting, with no final 0a byte. It is an ordinary read-only pipeline.
4. Interpret backslash escapes deliberately
GNU echo leaves backslashes alone by default. With no option, \n stays as two characters:
$ /usr/bin/echo 'first\nsecond'
first\nsecond
Use -e to enable the escape sequences listed by the installed manpage:
$ /usr/bin/echo -e 'first\nsecond\tready'
first
second ready
The supported forms:
\n,\t,\r: newline, horizontal tab, carriage return.\\: a literal backslash.\a: an alert.\c: stop output without writing the usual final newline.\0NNN: an octal byte, with one to three octal digits.\xHH: a hexadecimal byte, with one or two hexadecimal digits.
Use -E to state the default explicitly and keep the backslashes literal:
$ /usr/bin/echo -E 'path\tname'
path\tname
Tip
Do not rely on -e behaving identically in every shell's builtin. If the exact bytes matter in a script, prefer printf, whose format and arguments are designed for this job.
5. Stop option-like data changing the command
The GNU manpage warns that echo has trouble with strings that look like options. A value such as -n cannot reliably be told apart from the no-newline option when it is the first argument:
$ /usr/bin/echo -n
$ /usr/bin/echo -- '-n'
-- -n
The second command shows a GNU quirk: -- is printed as data here, not honoured as a portable option terminator. It does not make echo a generally safe argument printer. For data that may begin with a hyphen, use printf with a fixed format:
$ value='-n'
$ /usr/bin/printf '%s\n' "$value"
-n
The script controls the format string, while the value is always treated as text. That also stops percent sequences in the value being read as formatting instructions.
6. Verify without changing state
Use the built-in help and version options to confirm the installed interface:
$ /usr/bin/echo --version | head -1
echo (GNU coreutils) 9.4
$ /usr/bin/echo --help | head -4
Usage: /usr/bin/echo [SHORT-OPTION]... [STRING]...
or: /usr/bin/echo LONG-OPTION
Echo the STRING(s) to standard output.
Help text can vary with packaging and locale, so check the option names rather than expecting every line to match. A later command in a pipeline can also hide a non-zero status unless the shell's pipeline settings are configured for it. For a simple output test, inspect the result directly and then check the status:
$ /usr/bin/echo 'echo-ok'
echo-ok
$ printf 'exit status: %s\n' "$?"
exit status: 0
Tip
Echo does not need root, and adding sudo makes ownership, auditing and troubleshooting less clear. If output is being redirected to a protected file, solve the permission problem deliberately; do not run a whole script as root just to print a message.
Done means
- You know your echo. You checked whether the shell resolves
echoto a builtin or the GNU external command. - Output is verified. You can produce normal output with a trailing newline and check the result.
- Options are deliberate. You use
-n,-eand-Eonly when their effect is intended. - Option-like data is handled. You use
printf '%s\n'for reliable text output. - Nothing persistent changed. You used no elevated privileges.