159

I have the following variable.

echo "|$COMMAND|"

which returns

|
REBOOT|

How can I remove that first newline?

Benjamin W.
  • 38,596
  • 16
  • 96
  • 104
Matt Leyland
  • 1,989
  • 3
  • 14
  • 14

9 Answers9

155

Under , there are some bashisms:

The tr command could be replaced by // bashism:

COMMAND=$'\nREBOOT\r   \n'
echo "|${COMMAND}|"
|
   OOT
|

echo "|${COMMAND//[$'\t\r\n']}|"
|REBOOT   |

echo "|${COMMAND//[$'\t\r\n ']}|"
|REBOOT|

See Parameter Expansion and QUOTING in bash's man page:

man -Pless\ +/\/pattern bash
man -Pless\ +/\\\'string\\\' bash

man -Pless\ +/^\\\ *Parameter\\\ Exp bash
man -Pless\ +/^\\\ *QUOTING bash

Further...

As asked by @AlexJordan, this will suppress all specified characters. So what if $COMMAND do contain spaces...

COMMAND=$'         \n        RE BOOT      \r           \n'
echo "|$COMMAND|"
|
           BOOT      
|

CLEANED=${COMMAND//[$'\t\r\n']}
echo "|$CLEANED|"
|                 RE BOOT                 |

shopt -q extglob || { echo "Set shell option 'extglob' on.";shopt -s extglob;}

CLEANED=${CLEANED%%*( )}
echo "|$CLEANED|"
|                 RE BOOT|

CLEANED=${CLEANED##*( )}
echo "|$CLEANED|"
|RE BOOT|

Shortly:

COMMAND=$'         \n        RE BOOT      \r           \n'
CLEANED=${COMMAND//[$'\t\r\n']} && CLEANED=${CLEANED%%*( )}
echo "|${CLEANED##*( )}|"
|RE BOOT|

Note: have extglob option to be enabled (shopt -s extglob) in order to use *(...) syntax.

F. Hauri
  • 58,205
  • 15
  • 105
  • 122
  • 1
    This is the way to strip linefeeds from a variable in BASH. Other answers are needlessly invoking an extra TR process. BTW, it has the added benefit of removing ending spaces! – ingyhere Nov 12 '15 at 21:06
  • 2
    Note that it also removes *internal* spaces from a string as well... `COMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n ']}|"` returns `|REBOOT|` – Alex Jordan Mar 16 '16 at 11:48
  • 2
    @AlexJordan Yes, it's a *wanted feature*: You could whipe the space after `\n` to prevent this: `COMMAND="RE BOOT"; echo "|${COMMAND//[$'\t\r\n']}|"` will return `|RE BOOT|`. – F. Hauri Mar 16 '16 at 22:01
  • Fair enough - but I was trying to just trim the trailing spaces: `"RE BOOT \r"` turns into `"REBOOT"`, which is not what I wanted. Thought @ingyhere's comment was incomplete, so I dropped a comment! – Alex Jordan Mar 17 '16 at 12:40
  • @AlexJordan Answer edited: I've added a clean way to strip bounding spaces. – F. Hauri Apr 04 '17 at 14:23
  • 3
    How is the pattern working in `${COMMAND//[$'\t\r\n']}`? I thought `${COMMAND//[\t\r\n]}` would simply work, but it didn't. What is the `$` symbol for and the single quotes too? – haridsv May 11 '17 at 07:43
  • 1
    Regarding the $'' syntax, that's [ANSI C quoting](https://www.gnu.org/software/bash/manual/bash.html#ANSI_002dC-Quoting) (mentioned in wjordans answer). – chuckx Apr 19 '18 at 17:55
  • In VScode you can convert the line endings for the file from CRLF to LF and save it – Abhishek S May 25 '21 at 07:28
104
echo "|$COMMAND|"|tr '\n' ' '

will replace the newline (in POSIX/Unix it's not a carriage return) with a space.

To be honest I would think about switching away from bash to something more sane though. Or avoiding generating this malformed data in the first place.

Hmmm, this seems like it could be a horrible security hole as well, depending on where the data is coming from.

Robin Green
  • 30,802
  • 16
  • 100
  • 180
  • The date is coming from a curl request which is on the same server, How would i go about putting that into a new var ? newvar=$(echo "|$COMMAND|"|tr '\n' ' ') – Matt Leyland Oct 13 '13 at 13:46
  • 2
    Yes. But please tell me you're not allowing arbitrary people to reboot your server remotely without a password... – Robin Green Oct 13 '13 at 13:49
  • 48
    Why did you not use `tr -d '\n'` for *dropping* instead of *replace by a space* – F. Hauri Oct 13 '13 at 16:20
  • 3
    Please whipe useless pipes! Use `tr '\n' ' ' <<< "|$COMMAND|"` instead of `echo ... | ...` – F. Hauri Oct 13 '13 at 16:24
  • 13
    @F.Hauri: or useless tr's: `"|${COMMAND//$'\n'}|"` – rici Oct 13 '13 at 18:39
  • @rici: It already was my [answer](http://stackoverflow.com/a/19347380/1765658) ;-) – F. Hauri Oct 13 '13 at 22:28
  • @F.Hauri: Fair enough. I just didn't see what privileged pipe-removal over process-removal. Particularly since echo is builtin, so there's not a lot of difference between the echo and the << – rici Oct 13 '13 at 22:32
  • @rici: Try this: `time for i in {0..1000};do wc -c >/dev/null <</dev/null ;done` On my system, first take ~1.4 secs and second syntax take ~3.2 secs. – F. Hauri Oct 14 '13 at 07:02
  • tr '\n' ' ' <<< "|$COMMAND|" by F.Hauri - Worked fine. – Sergio Belevskij Mar 08 '16 at 03:53
  • Less efficient than substring subtitution in F. Hauri's answer, I think? – Alex Hall Feb 12 '20 at 06:40
96

Clean your variable by removing all the carriage returns:

COMMAND=$(echo $COMMAND|tr -d '\n')
Maxime Chéramy
  • 16,409
  • 7
  • 51
  • 73
  • 26
    Doesn't that strip linefeeds? Shouldn't it be `tr -d '\r'` instead? – Izzy Nov 17 '14 at 22:30
  • 3
    Echoing an uncommented variable removes all IFS characters (newline, space, tab by default). So if you're going to do this, you should be aware that *all* IFS characters are dropped, and you don't need the `tr`. Simply `COMMAND=$(echo $COMMAND)` will give a similar effect. Which is, presumably, devil spawn as it invokes a new process, but it's still pretty short and sweet for the human's eyes and if you have a second or two to spare in your life you may be willing to take the hit :-) . – Mike S Dec 27 '16 at 22:38
  • 1
    I've updated the question to say "linefeed", as its example did show that it was a linefeed, not a carriage return. This answer is still correct, but should maybe be updated to say "linefeed" instead of "carriage return"? – Benjamin W. Aug 07 '18 at 14:14
  • 1
    should have been the accepted answer combined with bash specific `${COMMAND//[$'\t\r\n']}` – Summer-Sky Nov 10 '21 at 16:36
13

Using bash:

echo "|${COMMAND/$'\n'}|"

(Note that the control character in this question is a 'newline' (\n), not a carriage return (\r); the latter would have output REBOOT| on a single line.)

Explanation

Uses the Bash Shell Parameter Expansion ${parameter/pattern/string}:

The pattern is expanded to produce a pattern just as in filename expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. [...] If string is null, matches of pattern are deleted and the / following pattern may be omitted.

Also uses the $'' ANSI-C quoting construct to specify a newline as $'\n'. Using a newline directly would work as well, though less pretty:

echo "|${COMMAND/
}|"

Full example

#!/bin/bash
COMMAND="$'\n'REBOOT"
echo "|${COMMAND/$'\n'}|"
# Outputs |REBOOT|

Or, using newlines:

#!/bin/bash
COMMAND="
REBOOT"
echo "|${COMMAND/
}|"
# Outputs |REBOOT|
wjordan
  • 18,662
  • 3
  • 79
  • 95
10

What worked for me was echo $testVar | tr "\n" " "

Where testVar contained my variable/script-output

Prachi
  • 520
  • 7
  • 27
9

Adding answer to show example of stripping multiple characters including \r using tr and using sed. And illustrating using hexdump.

In my case I had found that a command ending with awk print of the last item |awk '{print $2}' in the line included a carriage-return \r as well as quotes.

I used sed 's/["\n\r]//g' to strip both the carriage-return and quotes.

I could also have used tr -d '"\r\n'.

Interesting to note sed -z is needed if one wishes to remove \n line-feed chars.

$ COMMAND=$'\n"REBOOT"\r   \n'

$ echo "$COMMAND" |hexdump -C
00000000  0a 22 52 45 42 4f 4f 54  22 0d 20 20 20 0a 0a     |."REBOOT".   ..|

$ echo "$COMMAND" |tr -d '"\r\n' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

$ echo "$COMMAND" |sed 's/["\n\r]//g' |hexdump -C
00000000  0a 52 45 42 4f 4f 54 20  20 20 0a 0a              |.REBOOT   ..|

$ echo "$COMMAND" |sed -z 's/["\n\r]//g' |hexdump -C
00000000  52 45 42 4f 4f 54 20 20  20                       |REBOOT   |

And this is relevant: What are carriage return, linefeed, and form feed?

  • CR == \r == 0x0d
  • LF == \n == 0x0a
gaoithe
  • 3,877
  • 2
  • 27
  • 35
4

If you are using bash with the extglob option enabled, you can remove just the trailing whitespace via:

shopt -s extglob
COMMAND=$'\nRE BOOT\r   \n'
echo "|${COMMAND%%*([$'\t\r\n '])}|"

This outputs:

|
RE BOOT|

Or replace %% with ## to replace just the leading whitespace.

zeroimpl
  • 2,620
  • 20
  • 17
4

You can simply use echo -n "|$COMMAND|".

Paulo
  • 1,241
  • 2
  • 9
  • 25
2

To address one possible root of the actual issue, there is a chance you are sourcing a crlf file.

CRLF Example:

.env (crlf)

VARIABLE_A="abc"
VARIABLE_B="def"

run.sh

#!/bin/bash
source .env
echo "$VARIABLE_A"
echo "$VARIABLE_B"
echo "$VARIABLE_A $VARIABLE_B"

Returns:

abc
def
 def

If however you convert to LF:

.env (lf)

VARIABLE_A="abc"
VARIABLE_B="def"

run.sh

#!/bin/bash
source .env
echo "$VARIABLE_A"
echo "$VARIABLE_B"
echo "$VARIABLE_A $VARIABLE_B"

Returns:

abc
def
abc def
conmak
  • 867
  • 7
  • 12
  • 1
    Thank you for this, I've been pulling my hairs on this for a while wondering why variable substitution was messing my string. Was using a CRLF file on linux... – yonicsurny Mar 16 '21 at 10:21