10

Basically, I want to achieve something like the inverse of echo -e.

I have a variable which stores a command output, but I want to print newlines as \n.

3
  • 1
    Does the variable contain literal \ns or actual newlines that you want to convert to \ns? Commented May 24, 2014 at 18:24
  • @Biffen The variable contains actual newlines. Commented May 24, 2014 at 18:25
  • 1
    Strictly speaking, the inverse of echo -e would also involve translating additional control chars. into their escape sequences, such as tabs to '\t'. Based on the accepted answer, however, it seems that handling newlines is sufficient in this case. Commented May 25, 2014 at 3:56

4 Answers 4

11

Here's my solution:

sed 's/$/\\n/' | tr -d '\n' 
Sign up to request clarification or add additional context in comments.

2 Comments

That's simpler than the awk solution. I'll accept this one.
+1 - clever; as with the awk solution, you always get a trailing literal \n sequence; unlike with awk, however, suppressing it is simple: sed '$! s/$/\\n/'.
8

If your input is already in a (Bash) shell variable, say $varWithNewlines:

echo "${varWithNewlines//$'\n'/\\n}" 

It simply uses Bash parameter expansion to replace all newline ($'\n') instances with literal '\n' each.


If your input comes from a file, use AWK:

awk -v ORS='\\n' 1 

In action, with sample input:

# Sample input with actual newlines created with ANSI C quoting ($'...'), # which turns `\n` literals into actual newlines. varWithNewlines=$'line 1\nline 2\nline 3' # Translate newlines to '\n' literals. # Note the use of `printf %s` to avoid adding an additional newline. # By contrast, a here-string - <<<"$varWithNewlines" _always appends a newline_. printf %s "$varWithNewlines" | awk -v ORS='\\n' 1 
  • awk reads input line by line
  • by setting ORS- the output record separator to literal '\n' (escaped with an additional \ so that awk doesn't interpret it as an escape sequence), the input lines are output with that separator
  • 1 is just shorthand for {print}, i.e., all input lines are printed, terminated by ORS.

Note: The output will always end in literal '\n', even if your input does not end in a newline.

This is because AWK terminates every output line with ORS, whether the input line ended with a newline (separator specified in FS) or not.


Here's how to unconditionally strip the terminating literal '\n' from your output.

# Translate newlines to '\n' literals and capture in variable. varEncoded=$(printf %s "$varWithNewlines" | awk -v ORS='\\n' 1) # Strip terminating '\n' literal from the variable value # using Bash parameter expansion. echo "${varEncoded%\\n}" 

By contrast, more work is needed if you want to make the presence of a terminating literal '\n' dependent on whether the input ends with a newline or not.

# Translate newlines to '\n' literals and capture in variable. varEncoded=$(printf %s "$varWithNewlines" | awk -v ORS='\\n' 1) # If the input does not end with a newline, strip the terminating '\n' literal. if [[ $varWithNewlines != *$'\n' ]]; then # Strip terminating '\n' literal from the variable value # using Bash parameter expansion. echo "${varEncoded%\\n}" else echo "$varEncoded" fi 

Comments

1

You can use printf "%q":

eol=$'\n' printf "%q\n" "$eol" $'\n' 

2 Comments

Is there a way to not introduce the $' and ' around the string?
Just to clarify: depending on input, printf %q will EITHER output an overall unquoted strings with individual shell metacharacters \ -escaped, OR - if control characters such as newlines and others too (e.g., tabs) are present - an ANSI C-quoted string ($'...'), as in the answer. The purpose of printf % is to get a representation of a string that you can safely use as command arguments or variable values in source code - i.e., the output is usually NOT meant to be used literally.
1

A Bash solution

x=$'abcd\ne fg\nghi' printf "%s\n" "$x" abcd e fg ghi y=$(IFS=$'\n'; set -f; printf '%s\\n' $x) y=${y%??} printf "%s\n" "$y" abcd\ne fg\nghi 

2 Comments

Unfortunately, the unquoted reference to $x makes its value subject to pathname expansion, so that an input line such as * would yield unexpected results - you could include set -f; at the start of your subshell to suppress pathname expansion.
@mklement0, true enough. fixed

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.