Bash - 在可变长度的线条后面画一条垂直线

Bash - 在可变长度的线条后面画一条垂直线

我有一个具有以下格式的文本文件,我想在这些行后面添加一条垂直线,然后添加数字:

c4-1 d e c
c d e c
e-2 f g2
e4 f g2
g8-4\( a-5 g f\) e4 c
g'8\( a g f\) e4 c
c-1 r c2
c4 r c2 

我通过以下方式实现了行和编号while-loop

#!/bin/bash

while read -r line; do
    if [ -z "$line" ]; then
        echo
        continue
    fi
    n=$((++n)) \
    && grep -vE "^$|^%" <<< "$line" \
    | sed 's/$/\ \|\ \%'$(("$n"))'/'
done < file

并得到如下输出:

c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8

现在我希望添加内容垂直对齐并获得如下输出:

c4-1 d e c            | %1
c d e c               | %2
e-2 f g2              | %3
e4 f g2               | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c    | %6
c-1 r c2              | %7
c4 r c2               | %8

这意味着我需要以某种方式获取最长行的行长度(此处:21个字符)和每行的行长度并添加空格的差值,我怎样才能实现这一点?

答案1

您可以打印不对齐的行,并使用column -t虚拟分隔符格式化输出:

#!/bin/bash

while read -r line; do
  if [ -z "$line" ]; then
    echo
    continue
  fi
  printf '%s@| %%%s\n' "$line" "$((++n))"
done < file | column -e -s'@' -t | sed 's/ |/|/'

在这里,我在指示列结尾@之前添加了一个虚拟字符。末尾的|命令sed用于删除 之前的一个附加空格字符|。需要选项-e来在输出中保留空行。

输出:

c4-1 d e c            | %1
c d e c               | %2
e-2 f g2              | %3
e4 f g2               | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c    | %6
c-1 r c2              | %7
c4 r c2               | %8

答案2

使用awk+ GNUwc假设输入中的所有字符都是单宽:

$ awk -v f="$(wc -L < ip.txt)" '{printf "%-*s | %%%s\n", f, $0, NR}' ip.txt
c4-1 d e c            | %1
c d e c               | %2
e-2 f g2              | %3
e4 f g2               | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c    | %6
c-1 r c2              | %7
c4 r c2               | %8

答案3

普通 bash:适用于 bash 版本 >= 4.0

#!/bin/bash
mapfile -t lines < file
max=0
for line in "${lines[@]}"; do
    max=$(( ${#line} > max ? ${#line} : max ))
done
for i in "${!lines[@]}"; do
    printf "%-*s | %%%d\n" $max "${lines[i]}" $((i+1))
done

对于较旧的 bash 版本,请将 mapfile 替换为 while-read 循环:这适用于版本 3.2

#!/bin/bash
lines=()
max=0
while IFS= read -r line || [[ -n "line" ]]; do
    lines+=("$line")
    max=$(( ${#line} > max ? ${#line} : max ))
done < file
for i in "${!lines[@]}"; do
    printf "%-*s | %%%d\n" $max "${lines[i]}" $((i+1))
done

答案4

只是为了记录:(这非常慢,但这是我第一次尝试使用wc -L
肯定会使用 @Freddy 的答案column

#!/bin/bash

file="$1"

ll=$(wc -L < "$file")

while read -r line; do
    if [ -z "$line" ]; then
        echo
        continue
    fi
    sl=$(wc -L <<< "$line")
    if [ "$ll" = "$sl" ]; then
        as=$(echo "$ll - $sl" | bc)
    else
        as=$(echo "$ll - $sl + 1" | bc)
    fi
    space=$(printf '\ %.0s' $(seq "$as") )
    n=$((++n)) \
    && grep -vE "^$|^%" <<< "$line" \
    | sed "s/$/$space\ \|\ \%$(printf "%s" "$n")/"
done < "$file"

尽管它正在使用一个额外的空间:

c4-1 d e c             | %1
c d e c                | %2
e-2 f g2               | %3
e4 f g2                | %4
g8-4\( a-5 g f\) e4 c  | %5
g'8\( a g f\) e4 c     | %6
c-1 r c2               | %7
c4 r c2                | %8

相关内容