【问题标题】:Align trailing backslashes vertically垂直对齐尾部反斜杠
【发布时间】:2020-06-15 17:15:24
【问题描述】:

我有一个如下所示的文件:

abc \
 d      \
 efgh \
i
jklmnop \
q \rst \
uv
wx
y \
z

对于每组末尾有反斜杠的连续行,我想将这些反斜杠排列在一条垂直的直线上。因此,上述示例的预期输出为:

abc   \
 d    \
 efgh \
i
jklmnop \
q \rst  \
uv
wx
y \
z

我设法将 所有 反斜杠与该程序对齐:

$ awk '/\\$/ { sub(/\\$/,""); printf "%-20s\\\n",$0; next} 1' file
abc                 \
 d                  \
 efgh               \
i
jklmnop             \
q \rst              \
uv
wx
y                   \
z

但我不知道如何从这里开始,所以我正在寻求指导。我尝试在 SO 上进行搜索,但最好的结果都是关于删除尾随反斜杠。

关于实际输入的详细信息:

  • 行可以包含任何字符,包括反斜杠,并且由任意数量的字符组成,没有限制。
  • 最后一个反斜杠前后可能有多个空格和制表符。
  • 在最后一个反斜杠之前总是至少有一个空格或制表符。
  • 没有一行仅由反斜杠和零个或多个空格/制表符组成。

附:我不是在寻找 Perl 解决方案。

【问题讨论】:

  • 文件有多大?看起来它可能需要两次处理。另外,您如何确定此处的行长? d 有更多空格,但efgh 有更多字符长度。这里应该优先考虑哪一个?
  • @Inian 不超过 10 兆字节。最后一个非空白非制表符结束行
  • 你能澄清一下你自己管理的东西有什么问题吗?
  • 是否需要开始空格?
  • @Gilles 是的。还有anubhava,我不希望它看起来那样

标签: awk


【解决方案1】:

如果您在每行末尾的空格-然后-反斜线-然后-空格之前没有任何制表符:

$ cat tst.awk
FNR == 1 { prevHasEsc = blockNr = 0 }
hasEsc = match($0,/[[:space:]]*\\[[:space:]]*$/) {
    $0 = substr($0,1,RSTART-1)
    if ( ! prevHasEsc ) {
        ++blockNr
    }
}
{ prevHasEsc = hasEsc }
NR == FNR {
    if ( hasEsc ) {
        lgth[blockNr] = (lgth[blockNr] > length($0) ? lgth[blockNr] : length($0))
    }
    next
}
hasEsc {
    $0 = sprintf("%-*s \\", lgth[blockNr], $0)
}
{ print }

.

$ awk -f tst.awk file file
abc   \
 d    \
 efgh \
i
jklmnop \
q       \
r

如果你这样做了,我建议先通过pr -e -t 运行文本,以将制表符转换为相应数量的空格。

【讨论】:

    【解决方案2】:

    这是使用 gnu-awk 和自定义 RS 的一次尝试,它会中断每个以不带黑斜线结尾的子字符串的输入:

    awk -v RS='[^\n]*[^\\\\[:space:]][[:blank:]]*(\n|$)' '
    {
       sub(/\n$/, "", RT)
    }
    n = split($0, lines, /[[:blank:]]+\\[[:blank:]]*\n/) {
        lines[n] = RT
        mlen = 0
        # determine max length of a block
        for (i=1; i<=n; i++)
           if (mlen < length(lines[i]))
              mlen = length(lines[i])
        # print each segment with backslash at the end of max length
        for (i=1; i<n; i++)
           printf "%-" (mlen+1) "s\\\n", lines[i]
    }
    RT {
        print RT
    }' file
    

    abc   \
     d    \
     efgh \
    i
    jklmnop \
    q \rst  \
    uv
    wx
    y \
    z
    

    Code Demo

    详情:

    • -v RS='[^\n]*[^\\\\[:space:]][[:blank:]]*(\n|$):使用此正则表达式设置记录分隔符,它基本上匹配不以\ 结尾的行。因此,我们将获得每条记录中以 \ 结尾的所有连续行。
    • split($0, lines, /[[:blank:]]+\\[[:blank:]]*\n/:通过结束 \ 和以下换行符来拆分每条记录。
    • 在第一个for 循环中,我们循环遍历每个数组元素并确定行的最长长度,即mlen
    • 在第二个for 循环中,我们只使用mlen+1 作为长度打印每个线段以放置尾随\
    • 最后我们打印RT,这是我们作为RS=...的结果捕获的子字符串

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-24
      • 2013-02-28
      • 1970-01-01
      • 2012-01-06
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      相关资源
      最近更新 更多