7

所以......我知道我可以使用tac或其他一些工具来反转文件中的行顺序,但是如何在另一个维度(即水平方向)中重新排序?我正在尝试使用以下 awk 脚本来做到这一点:

{
    out="";
    for(i=length($0);i>0;i--) {
        out=out substr($0,i,1)}
    print out;
}

这似乎颠倒了字符,但它是乱码,我不明白为什么。我错过了什么?

我在 awk 中这样做,但有更好的方法吗? sed, 也许?

这是一个例子。输入数据如下所示:

$ cowsay <<<"hello"
 _______
< hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

输出如下所示:

$ cowsay <<<"hello" | rev
_______ 
> olleh <
------- 
^__^   \        
_______\)oo(  \         
\/\)       \)__(            
| w----||                
||     || 

请注意,无论我使用rev还是我自己的 awk 脚本,输出都是相同的。正如你所看到的,事情是颠倒的,但是......它被破坏了。

4

6 回答 6

10

rev很好,但它不填充输入行。它只是颠倒它们。

您看到的“修改”是因为一行可能有 20 个字符长,而下一行可能有 15 个字符长。在您的输入文本中,它们共享一个左侧列。但在您的输出文本中,他们需要共享右侧列。

所以你需要填充。哦,还有不对称反转,正如 Joachim 所说。

这是我的revawk

#!/usr/bin/awk -f

# 
length($0)>max {
    max=length($0);
}

{
    # Reverse the line...
    for(i=length($0);i>0;i--) {
        o[NR]=o[NR] substr($0,i,1);
    }
}

END {
    for(i=1;i<=NR;i++) {
        # prepend the output with sufficient padding
        fmt=sprintf("%%%ds%%s\n",max-length(o[i]));
        printf(fmt,"",o[i]);
    }
}

(我这样做了gawk;我认为我没有使用任何傻瓜,但如果您使用的是更经典的awk变体,您可能需要调整它。)

使用与使用 rev 相同的方式。

ghoti@pc:~$ echo hello | cowsay | ./revawk | tr '[[]()<>/\\]' '[][)(><\\/]'
                    _______ 
                   < olleh >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     ||                

如果您打算这样做,您甚至可以通过将 awk 脚本添加到最后printf一行来运行翻译:

        printf(fmt," ",o[i]) | "tr '[[]()<>/\\]' '[][)(><\\/]'";

但我不推荐它,因为它会降低该revawk命令对其他应用程序的用处。

于 2012-10-28T22:00:56.563 回答
5

你的线条长度不一样,所以倒转牛会打破它。您需要做的是将线“填充”为相同的长度,然后反转。

例如;

cowsay <<<"hello" | awk '{printf "%-40s\n", $0}' | rev

将其填充到 40 列,然后反转。

编辑:@ghoti 做了一个脚本,肯定会击败这个简单的反向,看看他的答案。

于 2012-10-28T22:00:38.567 回答
5

GNU awk这是使用和的一种方法rev

像这样运行:

awk -f ./script.awk <(echo "hello" | cowsay){,} | rev

内容script.awk

FNR==NR {
    if (length > max) {
        max = length
    }
    next
}

{
    while (length < max) {
        $0=$0 OFS
    }
}1

或者,这是单线:

awk 'FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS }1' <(echo "hello" | cowsay){,} | rev

结果:

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     ||                

-------------------------------------------------- ------------------------------------------

这是另一种使用方式GNU awk

像这样运行:

awk -f ./script.awk <(echo "hello" | cowsay){,}

内容script.awk

BEGIN {
    FS=""
}

FNR==NR { 
    if (length > max) {
        max = length
    }
    next
}

{
    while (length < max) {
        $0=$0 OFS
    }
    for (i=NF; i>=1; i--) {
        printf (i!=1) ? $i : $i ORS
    }
}

或者,这是单线:

awk 'BEGIN { FS="" } FNR==NR { if (length > max) max = length; next } { while (length < max) $0=$0 OFS; for (i=NF; i>=1; i--) printf (i!=1) ? $i : $i ORS }' <(echo "hello" | cowsay){,}

结果:

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     ||                

-------------------------------------------------- ------------------------------------------

解释:

这是第二个答案的解释。我假设有以下基本知识awk

FS=""                 # set the file separator to read only a single character
                      # at a time.

FNR==NR { ... }       # this returns true for only the first file in the argument
                      # list. Here, if the length of the line is greater than the
                      # variable 'max', then set 'max' to the length of the line.
                      # 'next' simply means consume the next line of input

while ...             # So when we read the file for the second time, we loop
                      # through this file, adding OFS (output FS; which is simply
                      # a single space) to the end of each line until 'max' is
                      # reached. This pad's the file nicely.

for ...               # then loop through the characters on each line in reverse.
                      # The printf statement is short for ... if the character is
                      # not at the first one, print it; else, print it and ORS.
                      # ORS is the output record separator and is a newline.

您可能需要了解的其他一些事项:

{,}通配符后缀是重复输入文件名两次的简写。不幸的是,它不是标准的 Bourne shell。但是,您可以改为使用:

<(echo "hello" | cowsay) <(echo "hello" | cowsay)

此外,在第一个示例中,{ ... }1{ ... print $0 }

HTH。

于 2012-10-28T23:21:16.267 回答
2

您也可以使用 bash、coreutils 和 sed 来实现(要使其与 zsh 一起使用,需要将 while 循环包装在 中tr ' ' '\x01' | while ... | tr '\x01' ' ',尚不知道为什么):

say=hello
longest=$(cowsay "$say" | wc -L)

echo "$say" | rev | cowsay | sed 's/\\/\\\\/g' | rev |
  while read; do printf "%*s\n" $longest "$REPLY"; done |
  tr '[[]()<>/\\]' '[][)(><\\/]'

输出:

                    _______ 
                   < hello >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     ||                

这在最后留下了很多多余的空间,追加| sed 's/ *$//'删除。

解释

需要引用 cowsay 输出,尤其是 sed 通过复制它们来处理的反斜杠。为了获得正确的线宽printf '%*s' len str,使用它len作为字符串长度参数。最后,不对称字符被对应的字符替换,如ghoti 的回答中所做的那样。

于 2012-10-29T09:13:11.167 回答
1

我不知道您是否可以在 AWK 中执行此操作,但以下是所需的步骤:

确定原件最长线的长度,您需要它为任何较小的线提供适当的间距。

    (__)\       )\/\

对于每行的最后一个字符,根据您从第一步中获得的内容来绘制行首空格的需求。

< hello >
//Needs ??? extra spaces, because it ends right after '>'.
//It does not have spaces after it, making it miss it's correct position after reverse.
        (__)\       )\/\
< hello >???????????????

对于每一行,应用该行所需的空格数,然后以相反的顺序添加原始字符。

                    _______ 
                   > olleh <
                    ------- 
            ^__^   \        
    _______\)oo(  \         
\/\)       \)__(            
   | w----||                
   ||     || 

最后,将所有非水平对称的字符替换为水平相反的字符。(<>[]等)

                    _______ 
                   < olleh >
                    ------- 
            ^__^   /        
    _______/(oo)  /         
/\/(       /(__)            
   | w----||                
   ||     || 

需要注意的两件事:

  • 如您所见,文本不会随着还原而正确。
  • 和 和$等字符不是水平对称的,但也可能没有对立面,除非您使用专门的 Unicode 块。%&
于 2012-10-28T22:34:28.980 回答
0

我会说你可能需要每行都是固定的列宽,所以每行都是相同的长度。因此,如果第一行是一个字符后跟一个 LF,则需要在反转之前用空格填充反转。

于 2012-10-28T22:00:25.580 回答