如何在bash中替换字符串中的空格和斜杠?

时间:2014-02-28 23:22:59

标签: regex bash optimization scripting tr

给予字符串:

foo='Hello     \    
World! \  
x

we are friends

here we are'

\字符之后或之前,还有制表符与空格混合。 我想只用空格替换空格,制表符和斜杠。我尝试过:

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

返回:

Hello World! x we are friend here we are 

我需要的结果是:

Hello World! x

we are friends

here we are

有什么想法,小费或技巧吗? 我能在一个命令中得到我想要的结果吗?

9 个答案:

答案 0 :(得分:3)

以下单行提供了所需的结果:

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

we are friends

here we are

说明:

tr '\n' '\r'从输入中删除换行符,以避免换行符的特殊sed行为。

sed 's,\s*\\\s*, ,g'将带有嵌入\的空格转换为一个空格。

tr '\r' '\n'放回未更改的换行符。

答案 1 :(得分:1)

尝试如下:

#!/bin/bash

foo="Hello     \
World!"

echo $foo | sed 's/[\s*,\\]//g'

答案 2 :(得分:1)

如果您只想打印输出,只需:

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

如果你想在存储在变量中的空格时挤压空格,那么其中一个:

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

perl版本的优点是它只删除行连续反斜杠(在行尾)。


请注意,当您使用双引号时,shell会处理行继续(斜杠后没有空格的正确行:

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

所以在这一点上,为时已晚。

如果使用单引号,则shell不会解释行继续,

$ 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

答案 3 :(得分:1)

foo='Hello     \    
World! \  
x

we are friends

here we are'

如果使用双引号,则shell会将\解释为行继续符。切换到单引号会保留字面反斜杠。

我在World!之后添加了一个反斜杠来连续测试多个反斜杠行。

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

输出:

Hello World! x

we are friends

here we are

这是做什么的?在伪代码中,您可以将其读作:

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

s/\n *//g                    # Remove all the newlines.

详细说明,这是它的作用:

  1. :s是一个标有s的分支,用于“开始”。
  2. s/( )? *\\ *$/\1/替换反斜杠及其周围的空格。如果有一个空格,则通过捕获( )?留下一个空格。
  3. 如果之前的替换失败,Te会跳转到标签e
  4. N连接以下行,包括换行符\n
  5. bs跳回到开头。这样我们就可以使用反斜杠处理多个连续的行。
  6. :e是标有e的“end”分支。
  7. s/\n *//g删除了第4步中的所有额外换行符。它还会从后续行中删除前导空格。
  8. 请注意T是GNU扩展。如果您需要在另一个版本的sed中使用它,则需要使用t。这可能需要额外的b标签或两个。

答案 4 :(得分:1)

您可以使用read循环来获得所需的输出。

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"

答案 5 :(得分:1)

使用awk

$ echo "$foo"
Hello     \
World! \
x

we are friends

here we are

使用尾随换行符:

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

we are friends

here we are
                                                                                              .

没有尾随换行符:

$ 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

答案 6 :(得分:1)

sed是一个很好的工具,可以在一行上进行简单的替换,但对于其他任何东西只需使用awk。这使用GNU awk进行多字符RS(其他awks RS='\0'适用于不包含NUL字符的文本文件):

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

we are friends

here we are

答案 7 :(得分:0)

使用诸如extended globbingparameter expansion之类的基础知识......但它可能同样丑陋

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

答案 8 :(得分:0)

据我所知,你想删除尾随空格后跟一个反斜杠转义的换行符?

在这种情况下,请使用正则表达式( ) *\\\n进行搜索并替换为\1