How can I delete a trailing newline in bash?

20,214

Solution 1

This should work:

printf "one\ntwo\n" | awk 'NR>1{print PREV} {PREV=$0} END{printf("%s",$0)}' ; echo " done"

The script always prints previous line instead of current, and the last line is treated differently.

What it does in more detail:

  1. NR>1{print PREV} Print previous line (except the first time).
  2. {PREV=$0} Stores current line in PREV variable.
  3. END{printf("%s",$0)} Finally, print last line withtout line break.

Also note this would remove at most one empty line at the end (no support for removing "one\ntwo\n\n\n").

Solution 2

You can use perl without chomp:

$ printf "one\ntwo\n" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done
$ printf "one\ntwo" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

But why not use chomp itself:

$ printf "one\ntwo\n" | perl -pe 'chomp if eof'; echo " done"

Solution 3

If you want an exact equivalent to chomp, the first method that comes to my mind is the awk solution that LatinSuD already posted. I'll add some other methods that don't implement chomp but implement some common tasks that chomp is often used for.

When you stuff some text into a variable, all newlines at the end are stripped. So all these commands produce the same single-line output:

echo "$(printf 'one\ntwo') done"
echo "$(printf 'one\ntwo\n') done"
echo "$(printf 'one\ntwo\n\n') done"
echo "$(printf 'one\ntwo\n\n\n\n\n\n\n\n\n\n') done"

If you want to append some text to the last line of a file or of a command's output, sed can be convenient. With GNU sed and most other modern implementations, this works even if the input doesn't end in a newline¹; however, this won't add a newline if there wasn't one already.

sed '$ s/$/ done/'

¹ However this doesn't work with all sed implementations: sed is a text processing tool, and a file that isn't empty and doesn't end with a newline character is not a text file.

Solution 4

I found bash was able to do what I wanted without any other tools. This isn't going to replicate chomp precisely but might help someone.

A command whose output ends with a newline will have it chomped during substitution:

the_output=$(my_command_with_a_newline)

It can be used as part of a pipeline with echo -n (quote the substitution to preserve the other newlines):

echo -n "$(my_command_with_a_newline)" |tail_or_whatever

Using printf "one\ntwo\n" like the OP:

$ echo -n "$(printf "one\ntwo\n")"; echo " done"
one
two done

Solution 5

Another perl approach. This one reads the entire input into memory so it might not be a good idea for large amounts of data (use cuonglm's or the awk approach for that):

$ printf "one\ntwo\n" | perl -0777pe 's/\n$//'; echo " done"
one
two done
Share:
20,214

Related videos on Youtube

Flimm
Author by

Flimm

Updated on September 18, 2022

Comments

  • Flimm
    Flimm 3 months

    I'm looking for something that behaves like Perl's chomp. I'm looking for a command that simply prints its input, minus the last character if it's a newline:

    $ printf "one\ntwo\n" | COMMAND_IM_LOOKING_FOR ; echo " done"
    one
    two done
    $ printf "one\ntwo" | COMMAND_IM_LOOKING_FOR ; echo " done"
    one
    two done
    

    (Command substitution in Bash and Zsh deletes all trailing new lines, but I'm looking for something that deletes one trailing new line at most.)

  • Flimm
    Flimm over 8 years
    This is not exactly equivalent to chomp, as chomp only deletes at most one trailing newline.
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 8 years
    @Flimm Yes, the most obvious exact equivalent to chomp would be the awk solution that LatinSuD already posted. But in many cases chomp is just a tool to do a job, and I provide ways to do some common tasks. Let me update my answer to clarify this.
  • terdon
    terdon about 3 years
    Thanks, @StéphaneChazelas, fixed. For some reason, this switch always confuses me!
  • Flimm
    Flimm over 2 years
    Bash will remove all trailing newlines in command substitution, not just one, see for yourself: foobar="$(echo -e 'hello\n\n\n'); printf %s "$foobar"