4

I have a multi line text file that I want to echo out as a single line file where the new lines are shown as \n.

Any pointers would be great.

  • 1
    something like this? seq 5 | awk -v ORS='\\n' '1' – Sundeep Jul 26 '17 at 9:55
7

Using the Perl sledgehammer:

$ perl -pe 's/\n/\\n/' < file.txt ; echo

The echo is there to print a trailing newline just in case it's wanted for display purposes. This changes the NL on the last line to \n too.

Or a rather straightforward solution in just Bash, assuming the file doesn't contain any embedded NUL bytes. Though note that this loads the whole file in memory:

$ IFS= read -r -d '' text < file.txt; printf '%s\n' "${text//$'\n'/\\n}"
  • you can avoid the echo using perl -pe 's/\n/\\n/; END{print "\n"}' or if last \n is not needed, perl -pe 's/\n/\\n/ if !eof' – Sundeep Jul 26 '17 at 11:41
  • @Sundeep, I know, but that's longer and uglier. :D I'm not sure what to think about the last line. – ilkkachu Jul 26 '17 at 12:49
5

Beware of a few special cases you may need to consider here. Let's take as example 4 files:

  • file1 an empty file
  • file2 a file with a single newline (␤) character (one empty line)
  • file3 a proper text file like:

    foo␤
    bar␤
    
  • file4 a file with the last line non-delimited:

    foo␤
    bar
    

If you want:

  • file1: empty
  • file2: \n␤
  • file3: foo\nbar\n␤
  • file4: foo\nbar␤

(for instance so that you can reconstruct the file by passing the output to printf %b).

Then, you could do:

perl -pe 's/\n/\\n/; END{print "\n" if $.}' < file

Or with GNU awk:

gawk '{printf "%s", $0 (RT ? "\\n" : "")}
      END{if (NR) print ""}' < file

You can adapt if you want to ignore the delimiter of the last line if any, or don't want the trailing newline, but then you may want to test on those 4 different cases that it does what you want as you'll probably find that you need to have special treatments for some of them.

For instance, your

< file sed '$!G' | paste -sd '\\n' -

solution to ignore the delimiter of the last line produces one empty line for both file1 and file2 (and for not-properly-delimited text (file4) may not work properly with all sed/paste implementations as those are meant to deal with valid text).

perl -pe 's/\n?$/eof ? "\n" : "\\n"/e' < file

may be more portable, and maybe slightly better for file1 and file2 which it would leave untouched.

  • I ended up using the solution in your original comment ` cat file.txt | sed '$!G' | paste -sd '\\n'` – Louise McMahon Jul 26 '17 at 12:27
  • @Louise, OK, though see edit. – Stéphane Chazelas Jul 26 '17 at 12:30
4

tr alone can't do it since you need to replace one character by two characters. sed alone can't do it easily since it outputs a newline at the end of everything it prints. But you can do it by combining the two: add \n at the end of every line, then remove the line breaks.

sed 's/$/\\n/' | tr -d '\n'

This produces a file that ends with \n and no line break. If you want a newline at the very end, echo it:

{ sed 's/$/\\n/' | tr -d '\n'; echo; }

(Note that this turns an empty file into a file with a single empty line.) If you don't want the last \n, sed it away:

sed 's/$/\\n/' | tr -d '\n' | sed 's/\\n$//'
2

The solution I used was.

< file.txt sed '$!G' | paste -sd '\\n' -
  • sed '$!G' appends an empty line to each line of input except the last ($) one.
  • paste -sd '\\n' pastes those resulting lines alternatively with backslash and n. And adds a newline in the end to result in valid text output.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.