从行延续中的字符串末尾删除换行符



我知道有几个不同的打开和回答,但我的有点不同。我正在尝试在 bash 中做到这一点。

我有这个文件:

Line1 asd asd asd 
asd asd asd 
Line2 asd asd asd 
asd asd asd 
Line3 asd asd asd 
asd asd asd 
Line4 asd asd asd 
asd asd asd 

我想要的结果是:

Line1 asd asd asd asd asd asd
Line2 asd asd asd asd asd asd
Line3 asd asd asd asd asd asd
Line4 asd asd asd asd asd asd

因此,作为 bash 循环更容易阅读。什么命令允许我这样做?

提前谢谢。

bash 内置read在您不使用-r时支持反斜杠连续行(其他情况下,当您需要此支持时,您应该始终使用-r)。

所以它应该从文件/等中读取这些行就可以了。(假设它们中没有需要保留的其他反斜杠转义序列。

$ while IFS= read line; do
echo "[$line]"
done < <(printf 'Line1 asd asd asd 
asd asd asd 
Line2 asd asd asd 
asd asd asd 
Line3 asd asd asd 
asd asd asd 
Line4 asd asd asd 
asd asd asd 
')
[Line1 asd asd asd     asd asd asd ]
[Line2 asd asd asd     asd asd asd ]
[Line3 asd asd asd     asd asd asd ]

Perl 解决方案:

perl -pe 's/\$// and chomp' < input > output
  • s///是一种替代。\匹配反斜杠,$匹配行尾。
  • chomp删除尾随换行符(如果存在)。

若要同时删除前导空格,请使用

's/^ +//; s/\$// and chomp'
  • ^与行首匹配。 +匹配一个或多个空格。

相反。

$ awk -v RS= '{gsub(/s*\s*/,"")}1' file
Line1 asd asd asd asd asd asd
Line2 asd asd asd asd asd asd
Line3 asd asd asd asd asd asd
Line4 asd asd asd asd asd asd

如果您没有 GNU awk,请使用[[:space:]]而不是s

请注意,任何时候您在 shell 中编写循环只是为了操作文本时,您都有错误的方法,因此在准备简化 bash 读取循环时执行上述操作可能是一个坏主意。

注意:

  • 下面的第一个解决方案反映了 OP 的特定空白处理要求;有关通用行继续处理,请参见底部
  • 这里的解决方案符合POSIX标准,因此它们应该适用于大多数类Unix平台(在OSX和Linux上验证)。
  • OP自己的解决方案建议输入具有Windows样式的行尾(rn)。但是,鉴于问题中没有说明这一点,这里的解决方案仅与Unix风格的解决方案(n)匹配。要匹配rn行尾,请将n替换为'"$(printf 'r')"'n(原文如此),或者在下面的sed命令中bash'$'r''n。(使用GNUsed,你可以简单地使用rn,但POSIXsed不承认r是一个转义序列)。

OP 自己的解决方案的更正版本,该解决方案还可以正确处理以行前面的结尾的行。

sed -e ':a' -e '$!{N;ba' -e '}; s/ \n[[:blank:]]*/ /g' filename
  • -e ':a' -e '$!{N;ba' -e '}'是一个常见的sed习语:一个将所有输入行一次读入模式空间(输入缓冲区)的循环 -BSDsed需要多个-e选项才能完成此操作(或者,或者,多行脚本)。
    • 请注意,示例输入在最后一个换行符之前也带有,这是不寻常的,并且会导致该不会被删除;如果您确实需要处理这种情况,请在上面的s/.../.../之前插入G;,这会有效地将另一个换行符附加到模式空间,因此也会导致最后一个被删除。
  • 然后,文本替换命令s/ \n[[:blank:]]*/ /g所有输入行上运行,全局 (g) 替换单个空格的运行,后跟(n),后跟换行符 ([[:blank:]]*),后跟任意数量的空格和/或制表符字符。 (<space>),并用单个空格 () 替换每个这样的运行。
    简而言之:在删除尾随sed并从下一行剥离前导空格后,awk行末尾会导致该行与下一行连接。

注意:

  • 以下解决方案有awk<<'EOF'两种口味。
  • 通常,EOF解决方案更可取,因为它们不会一次读取所有输入,这对于大文件可能会有问题。(可以说,它们也更容易理解。
  • 请注意,下面用作示例输入的 here-文档使用带引号的EOF 分隔符 (<newline>) 来保留字符串不变;如果不引用read,shell自己的字符串文字处理将解析嵌入的行继续并在命令看到字符串之前连接行

不带空格处理的泛型行继续处理:

这些解决方案只是删除read序列,从而按原样连接行,没有分隔符;例如,这就是默认执行的操作。

但是,与sed相比,这些解决方案有两个优点:

  • 线awk实例将保留。
  • awk/\$/的速度要快得多,而不仅仅是几行输入。

解决方案:

awk '/\$/ { printf "%s", substr($0, 1, length($0)-1); next } 1' <<'EOF'
Line1 starts here
and ends here.
Line2 starts here, 
continues here,
and ends here.
EOF
Line1 starts here and ends here.
Line2 starts here,  continues here,  and ends here.
  • $匹配线路末端()的substr($0, 1, length($0)-1),表示线路的延续。
  • $0从输入行中删除该尾随printf "%s"next
  • 通过使用1,打印(修改后的)当前行时没有尾随换行符,这意味着接下来出现的任何打印命令都将直接附加到它,有效地连接当前和下一行。
  • awk完成当前行的处理。
  • { print }是一个常见的n习语,是sed的简写,即简单地打印输入行(带有尾随bash)。

<newline>解决方案:

$ sed -e ':a' -e '$!{N;ba' -e '}; s/\n//g' <<'EOF'
Line1 starts here
and ends here.
Line2 starts here, 
continues here,
and ends here.
EOF 
Line1 starts here and ends here.
Line2 starts here,  continues here,  and ends here.

请注意最后一行中的两个双倍空格,因为所有空格都保留。

[不推荐] 纯(例如,awk)解决方案:

以下解决方案非常简单,并不完全可靠,并且存在安全风险:它可能导致执行任意命令

# Store input filename, passed as the 1st argument,
# in variable $file.
file=$1
# Construct a string that results in a valid shell command containing a
# *literal* here-document with *unquoted* EOF delimiter 0x3 - chosen so
# that it doesn't conflict with the input.
#
# When the resulting command is evaluated by `eval`, the *shell itself* 
# performs the desired line-continuation processing, BUT:
# '$'-prefixed tokens in the input, including command substitutions
# ('$(...)' and '`...`'), ARE EXPANDED, therefore:
# CAUTION: Maliciously constructed input can result in
#          execution of arbitrary commands.
eval "cat <<$(printf '3')
$(cat "$file")" 

使用空格规范化的泛型行继续处理:

这些解决方案按如下方式规范化空格:删除contd之前的任何尾随空格,下一行的前导空格也是如此;然后,生成的单个空格连接
参与行连续的行中的空格将按原样保留后者将这些解决方案与choroba的Perl解决方案区分开来

解决方案

awk '
contd { contd=0; sub(/^[[:blank:]]+/, "") } 
/\$/ { contd=1; sub(/[[:blank:]]*\$/, ""); printf "%s ", $0; next } 
1' <<'EOF'
Line1 starts here   
and ends here.
I am a loner. 
Line3 starts here,   
continues here,    
and ends here.
EOF
Line1 starts here and ends here.
I am a loner.
Line3 starts here, continues here, and ends here.
  • 变量contd(在布尔上下文中默认为 0/false)用作标志,以指示前一行是否以尾随sub(/^[[:blank:]]+/, "")发出行继续信号。
  • 如果设置了标志(模式$0),则会立即重置(尽管如果继续的行也在下一行继续,则可能会在下面再次设置),并且从当前行(/\$/)中删除前导空格; 请注意,不将目标变量指定为第三个参数会隐式针对整个输入行,.
  • $匹配线路末端()的contd=1,信令线路延续。
    • 因此,设置了标志(sub(/[[:blank:]]*\$/, "")),
    • 删除行尾之前的尾随空格(printf "%s "与该next本身一起,
    • 结果打印有尾随空格,但没有换行符,由1提供.
    • 然后awk继续到下一个输入行,而不处理当前行的进一步命令。
  • { print }是一个常见的n习语,是sed的简写,即简单地打印输入行(带有尾随);请注意,在两种情况下会到达此打印命令:
    • 任何不涉及行连续的行,打印时未修改
    • 由于第一个操作执行的修改,任何结束行延续的行(构成延续的一部分,但本身不继续下一行),打印时删除了前导空格。

sed ':a;N;$!ba;s/ \x0Dx0Ax09/ /g' filename解决方案

$ sed -e ':a' -e '$!{N;ba' -e '}; s/[[:blank:]]*\n[[:blank:]]*/ /g' <<'EOF'
Line1 starts here   
and ends here.
I am a loner.
Line3 starts here,   
continues here,    
and ends here.
EOF
Line1 starts here and ends here.
I am a loner.
Line3 starts here, continues here, and ends here.

行尾空格和行开头空格规范化为延续中涉及的行的单个空格。 请注意,没有尾随CC_90的行是如何原封不动地打印的。

编辑

此命令将带走下一行上的空格,反斜杠和制表符。

CC_91

line1 asd asd asd 
asd asd asd

line1 asd asd asd asd asd asd

然后我可以使用:

sed '/^[[:space:]]*$/d' filename

删除这些文件行之间不需要的空格

最新更新