sed help:根据下一行条件替换(美化ascii树输出)

时间:2014-07-30 05:34:38

标签: string awk sed

我有一个程序可以提供如下输出:

1405565344
  |
  +- 1405722995
  |   |
  |   +- 1405722998
  |   |   |
  |   |   +- 1405724849
  |   |     
  |   +- 1406051621
  |     
  +- 1406051709
  +- 1406733328


[END OF OUTPUT -- OUTPUT DOES NOT INCLUDE THIS LINE]

请注意,最后一行不是输出的一部分;如果我不在它们之后添加内容,则隐藏最后两行(通常为空白)。另请注意,最后一个条目后面的空白行上有多个空格,但SO不显示它们。

所以我想让它更紧凑,更好..漂亮。使用以下sed命令...

sed -e 's,|,│,g'     \
    -e "s,+,└,"      \
    -e "s,- ,─,"     \
    -e '/^[ │]*$/d'  

我可以将上面的输入转换为:

1405565344
  └─1405722995
  │   └─1405722998
  │   │   └─1405724849
  │   └─1406051621
  └─1406051709
  └─1406733328

好多了。理想情况下,我希望它是这样的:

1405565344
  ├─1405722995
  │   ├─1405722998
  │   │   └─1405724849
  │   └─1406051621
  ├─1406051709
  └─1406733328

我知道,差别很小,但它更有意义,更符合我程序的其他输出。

所以基本上:我想要一种让sed有条件地用+替换的方法,具体取决于它之后的行。看起来几乎是不可能的,没有完全不同的轨道。

有什么想法吗?

4 个答案:

答案 0 :(得分:2)

sed -n -e '/| *$/ d;1h;1!H
$ {x
:a
   s/\(\n[ |]*\)+\([^[:cntrl:]]*\1[|+]\)/\1├\2/;t a
:b
   s/\(\n[ |]*\)+/\1└/;t b
   s/|/│/g;s/- /─/g;p
   }' YourFile

应该做你的工作。使用其他字符进行测试而非图形化(不要传递给我的aix)。 如果从一行上的行开始直到T与下一个开头(第一个新行之后的那个)相同的模式,则测试变为+。我使用[:cntrl:]来捕获非换行符,所以如果有特殊的char被认为是控制字符,它就会失败(不要认为你的文件中有)。 如果不是这种情况(与+一致)改变角落中的de plus [为优化sed模式分组而修改] [为角焦点修改并且具有相同模式的2连续行的情况以+结尾]

答案 1 :(得分:1)

awk方式
可能会得到认真改进,但它可以按预期工作 我的机器无法显示,因此请将#L替换为它们。
如果有人有任何改进,请告诉我,我会更新!

awk '/\+/{a=$0;b=index($0,"+");next}
a{if(substr($0,b,1)=="|"){$0=gensub(/+/,"#","g",a)}else{$0=gensub(/+/,"L","g",a)}}
/[1-9]/{print $0}' file

输出

1405565344
#- 1405722995
|   #- 1405722998
|   |   L- 1405724849
|   L- 1406051621
L- 1406051709

答案 2 :(得分:1)

这样做。 gawk的match()函数设置变量RSTART。我检查以下行以查看该位置的字符。

gawk '
    function g(line) {
        gsub(/#/,   "├", line)
        gsub(/-/,   "─", line)
        gsub(/[+]/, "└", line)
        gsub(/[|]/, "│", line)
        return line
    }
    /^[[:blank:]|]*$/ {next} 
    prev {
        while (match(prev, /[+]/)) {
            c=substr($0, RSTART, 1); 
            if (c == "+" || c == "|")
                sub(/[+]/, "#", prev)
            else 
                break
        }
        print g(prev)
    }
    {prev=$0} 
    END {print g($0)}
' file

行动中:

$ echo "1405565344
  |
  +- 1405722995
  |   |
  |   +- 1405722998
  |   |   |
  |   |   +- 1405724849
  |   |     
  |   +- 1406051621
  |   +- foobar
  |     
  +- 1406051709
  +- barfoo" |
awk '
    function g(line) {
        gsub(/[+]/, "└", line)
        gsub(/#/, "├", line)
        gsub(/-/,"─", line)
        gsub(/[|]/, "│", line)
        return line
    }
    /^[[:blank:]|]*$/ {next} 
    prev {
        while (match(prev, /[+]/)) {
            c=substr($0, RSTART, 1); 
            if (c == "+" || c == "|")
                sub(/[+]/, "#", prev)
            else 
                break
        }
        print g(prev)
    }
    {prev=$0} 
    END {print g($0)}
'
1405565344
  ├─ 1405722995
  │   ├─ 1405722998
  │   │   └─ 1405724849
  │   ├─ 1406051621
  │   └─ foobar
  ├─ 1406051709
  └─ barfoo

答案 3 :(得分:0)

我的逻辑是使用awk,|作为标记化器并使用$ NF的长度来决定要打印的行

cat <ip_file.txt> | awk -F'|' '{if(length($NF)>5)print $0;}'

我输入了以下输出

1405565344
  +- 1405722995
  |   +- 1405722998
  |   |   +- 1405724849
  |   +- 1406051621
  +- 1406051709

注意:替换+ - 仍处于待决状态