在bash - line continuations中从字符串末尾删除换行符

时间:2015-05-05 13:00:13

标签: bash perl awk sed newline

我知道有几个不同的开放和回答,但我的有点不同。我试图在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循环更容易阅读。什么命令可以让我这样做?

提前致谢。

5 个答案:

答案 0 :(得分:3)

Perl解决方案:

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

要删除前导空格,请使用

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

代替。

答案 1 :(得分:3)

当您不使用read时,bash内置-r支持反斜杠 - 续行(其他当您需要此支持时,您总是应该使用-r

所以应该从文件/ etc中读取这些行。正好。 (假设他们没有其他反斜杠转义序列,需要保留它们。

$ 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 ]

答案 2 :(得分:1)

注意:

  • 下面的第一个解决方案反映了OP的特定空白处理要求; 查看通用行继续处理的底部
  • 此处的解决方案 POSIX兼容,因此它们应该适用于大多数类Unix平台(在OSX和Linux上验证)。
  • OP's own solution表示输入具有 Windows 样式的行结尾(\r\n)。但是,鉴于此问题尚未说明,此处的解决方案仅匹配 Unix 式(\n)。要匹配\r\n行结尾,请将\n替换为'"$(printf '\r')"'\n(原文如此),或者在bash'$'\r''\n替换sed命令。 (使用 GNU sed,您只需使用\r\n,但POSIX sed不会将\r识别为转义序列。

更正后的OP's own solution 版本,该版本还处理以\结尾的行,这些行正确地在行之前。

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

注意:

  • 以下解决方案包含\awk两种风格。
  • 通常情况下,sed解决方案更受欢迎,因为他们不会同时读取所有输入,这对于大文件可能会有问题。 (可以说,它们也更容易理解。)
  • 请注意,下面用作示例输入的here-documents使用引用的 EOF分隔符(awk)来保留未修改的字符串;在没有引用<<'EOF'的情况下, shell 自己的字符串文字处理将解析嵌入的行继续并在命令看到字符串之前连接行。

通用行继续处理没有空白处理:

这些解决方案只需删除 EOF序列,然后按加入 no separator < / em>的;例如,这是\<newline>默认执行的操作。

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

  • Line- 内部 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

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

[未推荐]纯 shell (例如,$ 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. )解决方案:

以下解决方案诱人简单,但不完全健壮并且安全风险可能会导致执行任意命令

bash

使用规范化空白的通用行继续处理:

这些解决方案规范化空白,如下所示:删除# 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")" 之前的任何尾随空格,以及 next 行中的空白空格;然后,生成的行通过单个空格 连接 后者将这些解决方案与choroba's Perl solution

区分开来

\<newline>解决方案

awk
  • 变量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. (在布尔上下文中默认为0 / false)用作标志,以指示前一行是否用尾随contd表示行继续。
  • 如果设置了标志(模式\),它会立即重置(尽管如果继续行继续在下一行继续,它可能会再次设置在下面),并且从中删除前导空格当前行(contd);请注意,未指定目标变量作为第3个参数隐式地以整个输入行sub(/^[[:blank:]]+/, "")
  • 为目标
  • $0匹配行末(/\\$/)的\,信令行延续。
    • 因此,该标志已设置($),
    • 在删除行结束contd=1之前跟踪空格(\以及sub(/[[:blank:]]*\\$/, "")本身
    • 并且结果打印了一个尾随空格,但没有换行符,由\提供。
    • printf "%s "然后进入下一个输入行,而不处理当前行的其他命令。
  • next是一种常见的1成语,是awk的简写,即简单地打印输入行(尾随{ print });请注意,在两种情况下达到此打印命令:
    • 任何未参与续行的行,未修改
    • 结束行续行的任何行(构成续行的一部分但不会在下一行继续),由于第一行执行了修改而删除了前导空格动作。

\n解决方案

sed

对于连续中涉及的行,将行尾和行开始空格标准化为单个空格。 请注意如何不修改没有尾随$ 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. 的行。

答案 3 :(得分:1)

$ 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读取循环可能是一个坏主意。

答案 4 :(得分:-1)

修改

此命令将删除下一行的空格,反斜杠和制表符。

sed ':a;N;$!ba;s/ \\\x0D\x0A\x09/ /g' filename

line1 asd asd asd \
     asd asd asd

line1 asd asd asd asd asd asd

然后我可以使用:

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

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