Issue
I have the following variable.
echo "|$COMMAND|"
which returns
|
REBOOT|
How can I remove that first newline?
Solution
In string, character replacement / deletion
Under bash, there are some bashisms:
The tr
command could be replaced by ${parameter/pattern/string}
with ANSI C standard
bashism:
COMMAND=$'\nREBOOT\r \n'
echo "|${COMMAND}|"
|
OOT
|
echo "|${COMMAND//[$'\t\r\n']}|"
|REBOOT |
echo "|${COMMAND//[$'\t\r\n ']}|"
|REBOOT|
See Parameter Expansion and ANSI C standard in bash's man page:
man bash | sed '/^$/{ x;\|parameter/pattern/string|{s/^\n*//;p};s/.*//;h;};H;d'
${parameter/pattern/string} ${parameter//pattern/string} ${parameter/#pattern/string} ${parameter/%pattern/string} Pattern substitution. The pattern is expanded to produce a pattern just as in pathname expansion. Parameter is ex‐ panded and the longest match of pattern against its value is replaced with string. string undergoes tilde expansion, pa‐ rameter and variable expansion, arithmetic expansion, com‐ mand and process substitution, and quote removal. The match is performed using the rules described under Pattern Match‐ ing below. In the first form above, only the first match is replaced. If there are two slashes separating parameter and pattern (the second form above), all matches of pattern are replaced with string. If pattern is preceded by # (the third form above), it must match at the beginning of the ex‐ panded value of parameter. If pattern is preceded by % (the fourth form above), it must match at the end of the expanded value of parameter. If the expansion of string is null, matches of pattern are deleted. If string is null, matches of pattern are deleted and the / following pattern may be omitted.
man bash | sed '/^$/{ x;/ANSI \+C \+standard/{s/^\n*//;p};s/.*//;h;};H;d'
man bash | sed '/^$/{ x;/ANSI[[:space:]]\+C[[:space:]]\+standard/{s/^\n*//;p};s/.*//;h;};H;d'
Character sequences of the form $'string' are treated as a special variant of single quotes. The sequence expands to string, with backslash-escaped characters in string replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are de‐ coded as follows: \a alert (bell) \b backspace \e \E an escape character \f form feed \n new line \r carriage return \t horizontal tab \v vertical tab \\ backslash \' single quote \" double quote \? question mark \nnn the eight-bit character whose value is the octal value nnn (one to three octal digits) \xHH the eight-bit character whose value is the hexadeci‐ mal value HH (one or two hex digits) \uHHHH the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits) \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH (one to eight hex dig‐ its) \cx a control-x character
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 (following${parameter/pattern/string}
syntax) - The
pattern
is/[$'\r\n ']
, begin with/
then all matches of pattern are replaced withstring
- Then, the replacement
string
is empty (as there a no second/
followed by anystring
...).
1 step more further
If you try to replace nothing
by something, for sample two consecutive spaces (then you could add two more spaces after replaced string in order to balance output):
echo "|${COMMAND//*()/ } |"
| R E B O O T |
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!!
Note: In fact: var=$(echo | tr x y)
implie two forks, not only one! By using following syntax, you will avoid 1 (x1000) fork, so this could be a little quicker:
time for i in {1..1000};do
COMMAND=$'\nREBOOT\r \n'
COMMAND=$( tr -d '\n\t\r ' <<<"$COMMAND" )
done;echo ${COMMAND@Q}
real 0m2.181s
user 0m1.590s
sys 0m0.566s
'REBOOT'
But still a lot overkill compared to the pure bash method.
Answered By - F. Hauri - Give Up GitHub Answer Checked By - Willingham (WPSolving Volunteer)