173

I have the following variable.

echo "|$COMMAND|"

which returns

|
REBOOT|

How can I remove that first newline?

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Matt Leyland
  • 2,149
  • 3
  • 15
  • 16

12 Answers12

177

In string, character replacement / deletion

Under , there are some bashisms:

The tr command could be replaced by ${parameter/pattern/string} 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\ +/parameter/pattern/string bash

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

man -Pless\ +/^\\\ *Parameter\\\ Exp bash
man -Pless\ +/^\\\ *QUOTING bash
${parameter//pattern/string}
    If there are two slashes separating parameter and pattern,
    all  matches of pattern are replaced with string.

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      
|

read -r COMMAND <<<"${COMMAND//[$'\t\r\n']}"
echo "|$COMMAND|"
|RE BOOT|

Explanation

Answering Vulwsztyn's question:

Why does this work when the pattern is empty?

In ${COMMAND//[$'\t\r\n ']} :

  • The 1st slashe / mean: Pattern substitution
  • The pattern is /[$'\r\n '], begin with / then all matches of pattern are replaced with string
  • Then, the string is empty.

Avoid fork to tr for single string!

Let compare:

COMMAND=$'\nREBOOT\r   \n'
echo ${COMMAND@Q}
$'\nREBOOT\r \n'

COMMAND=$(echo $COMMAND|tr -d '\n\t\r ')
echo ${COMMAND@Q}
'REBOOT'

Then

time for i in {1..1000};do
    COMMAND=$'\nREBOOT\r   \n'
    COMMAND=$(echo $COMMAND|tr -d '\n\t\r ')
done;echo ${COMMAND@Q}

real    0m2.785s
user    0m2.296s
sys     0m0.774s
'REBOOT'

With

COMMAND=$'\nREBOOT\r   \n'
COMMAND="${COMMAND//[$'\t\r\n ']}"
echo ${COMMAND@Q}

time for i in {1..1000};do
    COMMAND=$'\nREBOOT\r   \n'
    COMMAND="${COMMAND//[$'\t\r\n ']}"
done;echo ${COMMAND@Q}

real    0m0.006s
user    0m0.001s
sys     0m0.004s
'REBOOT'

Doing 1'000 forks to tr take more than 2700ms on my host, while same job is done in 6ms ( 464.2x faster!! ) by using built-in bash Parameter Expansion!!

F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
  • 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 - Give Up GitHub 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 - Give Up GitHub Apr 04 '17 at 14:23
  • 4
    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 – Abercrombie May 25 '21 at 07:28
  • @AlexJordan My answer to you're [previous comment](https://stackoverflow.com/questions/19345872/how-to-remove-a-newline-from-a-string-in-bash/19347380#comment59718553_19347380) was modified: Using `read`, there is no need to use extglob anymore. – F. Hauri - Give Up GitHub May 05 '23 at 10:04
  • Why does this work when the pattern is empty? – Vulwsztyn Jun 15 '23 at 16:44
  • https://stackoverflow.com/a/34789399/7195666 – Vulwsztyn Jun 15 '23 at 16:51
  • 1
    @Vulwsztyn Answer edited regarding your comment/question. – F. Hauri - Give Up GitHub Jun 15 '23 at 19:23
119

Clean your variable by removing all the linefeeds:

COMMAND=$(echo $COMMAND|tr -d '\n')
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
Maxime Chéramy
  • 17,761
  • 8
  • 54
  • 75
  • 29
    Doesn't that strip linefeeds? Shouldn't it be `tr -d '\r'` instead? – Izzy Nov 17 '14 at 22:30
  • 6
    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
  • 1
    @MikeS `+1` for noticing the antipattern `echo $COMMAND` (missing quotes). But _it's an antipattern and shouldn't be used_ since it will also expand glob characters if there are any (e.g., `*`, or `[...]` or `?`). So never use an unquoted expansion, _unless your variable is in fact a glob that you want to actually expand._ – gniourf_gniourf Jan 06 '23 at 10:18
108
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
  • 32,079
  • 16
  • 104
  • 187
15

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
  • 19,770
  • 3
  • 85
  • 98
12

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
  • 4,218
  • 3
  • 30
  • 38
10

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

Where testVar contained my variable/script-output

Prachi
  • 528
  • 8
  • 31
5

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,746
  • 22
  • 19
4

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

$ man echo

-n do not output the trailing newline

Paolo
  • 20,112
  • 21
  • 72
  • 113
Paulo
  • 1,458
  • 2
  • 12
  • 26
  • 1
    But at least one of the newlines under concern was not at the end. So this doesn't help. – Mike S Jan 06 '23 at 23:01
  • With all due respect: have you already tried my solution and checked whether or not it solved the original question? – Paulo Jan 07 '23 at 17:53
  • No, because the man page is quite clear: -n does not output the trailing newline. That's it, no more no less. If there's an embedded newline- which there is in the original question- then echo -n won't help. I did try it just now, and it behaves exactly as described. Embedded newlines remain; it does not "remove that first newline" which is what the original question asked. In bash, btw. – Mike S Jan 07 '23 at 18:01
  • Well, I got your point. When I run the command I suggested, the output is the same when you remove the first, even though the command I suggested is not intended for that. I believe I misunderstood the question. – Paulo Jan 09 '23 at 14:46
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
  • 1,200
  • 10
  • 13
  • 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... – Yonic Surny Mar 16 '21 at 10:21
2

Use this bashism if you don't want to spawn processes like (tr, sed or awk) for such a simple task. Bash can do that alone:

COMMAND=${COMMAND//$'\n'/}

From the documentation:

${FOO//from/to} Replace all
${FOO/from/to}  Replace first match
peterh
  • 11,875
  • 18
  • 85
  • 108
Ingo Baab
  • 528
  • 4
  • 7
  • You're posting 9 years after the answer was posted with those very bashisms, with excellent examples, with awesome pointers to the documentation. F. Hauri had the best answer in 2013, and this one adds nothing to it while leaving out some nice extras. Sorry, -1. – Mike S Jan 06 '23 at 23:07
  • @MikeS Sorry, if we want to write effective bash scripts, it is important to use the least possible amount of external binary executions. That means, we need to handle things with the internal bash meachnisms as possible. This is the first and only answer what does it. Many active bash users do not see any bad in the external calls, others yes. This dualism is very characteristic also on the Unix SE. If you do not have a strong affection _for_ calling the most possible external binaries, I think this answer is not so bad even on your opinion. – peterh Feb 22 '23 at 15:26
  • @peterh do you not see F. Hauri's answer? He uses the same construct. For example: `CLEANED=${COMMAND//[$'\t\r\n']}` . I didn't downvote because of its effectiveness in writing bash scripts, I downvoted because it shows up before F. Hauri's answer, which is more complete and includes pointers to the man pages and so on. It adds nothing new. – Mike S Feb 28 '23 at 17:58
  • @MikeS Well I overlooked it because its complexity. I searched for a quick solution only with internal commands. It is good to you if you can make a living by shell scripts. – peterh Mar 01 '23 at 06:08
0

Note that the shell already does that for you if you pass $COMMAND as a parameter instead of a string.

So you can try this:

COMMAND="a
   b
   c"

echo $COMMAND

To add the pipes, you can just use the quotes and no spaces like so:

echo "|"$COMMAND"|"

works just fine and it's a tad simpler than the other solutions. It also works with just /bin/sh.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
-2

Grep can be used to filter out any blank lines. This is useful if you have several lines of data and want to remove any empty.

echo "$COMMAND" | grep .
killjoy
  • 3,665
  • 1
  • 19
  • 16