如何在 bash 中替换空格和斜杠?



给出字符串:

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

此外,还有制表符字符前后的空格混合在一起。 我只想用一个空格替换空格、制表符和斜杠。我尝试过:

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

返回:

Hello World! x we are friend here we are 

我需要的结果是:

Hello World! x
we are friends
here we are

一些想法,提示或技巧来做到这一点? 我可以只用一个命令得到我想要的结果吗?

以下单行代码给出了所需的结果:

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'放回未更改的换行符。

尝试如下:

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

如果您只想按给定的方式打印输出,则只需:

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*ns*)/ /sg'
Hello World!
here we are
foo='Hello         
World!   
x
we are friends
here we are'

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

我在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表示"结束"的分支。
  7. s/n *//g从步骤 #4 中删除所有多余的换行符。它还会从后行中删除前导空格。

请注意,T是一个 GNU 扩展。如果需要在另一个版本的 sed 中使用它,则需要改用t。这可能需要额外的一两个b标签。

您可以使用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"

withawk

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

使用尾随换行符:

$ echo "$foo" | awk '{gsub(/[[:space:]]*\[[:space:]]*/," ",$0)}1' RS= FS='n' ORS='nn'
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) ? "nn" : "n")
}' RS= FS='n' 
Hello World! x
we are friends
here we are

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

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

使用扩展通配、参数扩展等基础...但它可能同样丑陋

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

据我了解,您只想删除尾随空格,后跟反斜杠转义的换行符?

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

相关内容

  • 没有找到相关文章