给出字符串:
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.
详细地说,这是它的作用:
:s
是一个标记为"开始"的s
分支。s/( )? *\ *$/1/
替换反斜杠及其周围的空格。如果有一个空间,它会通过捕获( )?
留下一个空间。- 如果之前的替换失败,
Te
跳转到标签e
。 N
连接以下行,包括换行符n
。bs
跳回到起点。这样我们就可以用反斜杠处理多个连续的行。:e
是一个标记为e
表示"结束"的分支。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