How to replace spaces and slash in string in bash?

Providing a string:

foo='Hello \ World! \ x we are friends here we are' 

Suppose there are tabs mixed with spaces after or before the \ character. I want to replace spaces, tabs, and slashes with just spaces. I tried:

 echo "$foo" | tr "[\s\t]\\\[\s\t]\n\[\s\t]" " " | tr -s " " 

Return:

 Hello World! x we are friend here we are 

And I need the result:

 Hello World! x we are friends here we are 

Some ideas, tips or tricks to do this? Can I get the result that I want to get only in the team?

+6
source share
9 answers

The following single-line layer gives the desired result:

 echo "$foo" | tr '\n' '\r' | sed 's,\s*\\\s*, ,g' | tr '\r' '\n' Hello World! we are friends here we are 

Explanation:

tr '\n' '\r' removes newlines from input to avoid sed special behavior for newlines.

sed 's,\s*\\\s*, ,g' converts spaces with an embedded \ into one space.

tr '\r' '\n' returns unchanged newline strings.

+2
source

Try the following:

 #!/bin/bash foo="Hello \ World!" echo $foo | sed 's/[\s*,\\]//g' 
+1
source

If you want to print the output as given, you just need to:

 foo='Hello \ World!' bar=$(tr -d '\\' <<<"$foo") echo $bar # unquoted! 
 Hello World! 

If you want to compress spaces, as they are stored in a variable, then one of:

 bar=$(tr -d '\\' <<<"$foo" | tr -s '[:space:]' " ") bar=$(perl -0777 -pe 's/\\$//mg; s/\s+/ /g' <<<"$foo") 

The advantage of the perl version is that it only removes backtraces of the line continuation (at the end of the line).


Note that when you use double quotes, the shell takes care of the continuation of the line (correct, without spaces after the slash:

 $ foo="Hello \ World" $ echo "$foo" Hello World 

So, at this point it is too late.

If you use single quotation marks, the shell will not interpret line extensions, but

 $ foo='Hello \ World! here we are' $ echo "$foo" Hello \ World! here we are $ echo "$foo" | perl -0777 -pe 's/(\s*\\\s*\n\s*)/ /sg' Hello World! here we are 
+1
source
 foo='Hello \ World! \ x we are friends here we are' 

If you use double quotes, then the shell will interpret the \ character as a line continuation character. Switching to single quotes preserves a literal backslash.

I added a backslash after World! to check for multiple backslash lines per line.

 sed -r ':s; s/( )? *\\ *$/\1/; Te; N; bs; :e; s/\n *//g' <<< "$foo" 

Output:

 Hello World! x we are friends here we are 

What does it do? In pseudo code, you can read this as:

 while (s/( )? *\\ *$/\1/) { # While there a backslash to remove, remove it... N # ...and concatenate the next line. } s/\n *//g # Remove all the newlines. 

In detail, here is what he does:

  • :s is the branch labeled s for "start".
  • s/( )? *\\ *$/\1/ s/( )? *\\ *$/\1/ replaces the backslash and surrounding spaces. Does it leave one space, if there is one, capturing ( )? .
  • If the previous lookup failed, Te goes to label e .
  • N concatenates the next line, including the new line \n .
  • bs goes back to the beginning. This means that we can process multiple consecutive lines with backslashes.
  • :e is the branch labeled e for "end".
  • s/\n *//g removes all additional lines of the newline from step 4. It also removes leading spaces from the next line.

Note that T is an extension of GNU. If you need this to work in a different version of sed, you need to use T This is likely to take an extra label b or two.

+1
source

You can use the read loop to get the result you want.

 arr=() i=0 while read line; do ((i++)) [ $i -le 3 ] && arr+=($line) if [ $i -eq 3 ]; then echo ${arr[@]} elif [ $i -gt 3 ]; then echo $line fi done <<< "$foo" 
+1
source

With awk :

 $ echo "$foo" Hello \ World! \ x we are friends here we are 

With the ending new line:

 $ echo "$foo" | awk '{gsub(/[[:space:]]*\\[[:space:]]*/," ",$0)}1' RS= FS='\n' ORS='\n\n' Hello World! x we are friends here we are . 

Without a final new line:

 $ echo "$foo" | awk '{ gsub(/[[:space:]]*\\[[:space:]]*/," ",$0) a[++i] = $0 } END { for(;j<i;) printf "%s%s", a[++j], (ORS = (j < NR) ? "\n\n" : "\n") }' RS= FS='\n' Hello World! x we are friends here we are 
+1
source

sed is a great tool for simple sub-sections on one line, but just use awk for anything else. This uses GNU awk for multi-char RS (with other awks RS='\0' will work for text files that do not contain NUL characters):

 $ echo "$foo" | awk -v RS='^$' -v ORS= '{gsub(/\s+\\\s+/," ")}1' Hello World! x we are friends here we are 
+1
source

With basinisms such as extended globbing , parameter expansion , etc., but this is probably just as ugly

 foo='Hello \ World!' shopt -s extglob echo "${foo/+( )\\*( )$'\n'/ }" Hello World! 
0
source

As I understand it, you just want to remove trailing spaces followed by a backslash with a new line?

In this case, do a search with the regular expression ( ) *\\\n and replace with \1

0
source

All Articles