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いいですが、入力行を埋めません。それはそれらを逆転させるだけです。

表示されている「マングリング」は、1行の長さが20文字で、次の行の長さが15文字である可能性があるためです。入力テキストでは、左側の列を共有しています。ただし、出力テキストでは、右側の列を共有する必要があります。

したがって、パディングが必要です。ああ、そしてヨアヒムが言ったように、非対称の逆転。

これが私の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;私はgawkismを使用したとは思いませんが、より古典的な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とを使用する 1 つの方法を次に示します。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----||                
   ||     ||                

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

説明:

これが2番目の答えの説明です。次の基本的な知識があることを前提としてい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.

あなたが知る必要があるかもしれない他のいくつかのこと:

ワイルド{,}カード サフィックスは、入力ファイル名を 2 回繰り返すための省略形です。残念ながら、これは標準の Bourne シェルではありません。ただし、代わりに次を使用できます。

<(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/ *$//'して削除します。

説明

カウセイの出力、特に sed が複製して処理するバックスラッシュを引用する必要があります。正しい線幅を取得するには、文字列の長さパラメーターとしてprintf '%*s' len str使用されます。len最後に、非対称の文字は、ghiti の回答で行われたように、対応する文字に置き換えられます。

于 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----||                
   ||     || 

次の 2 つの点に注意してください。

  • ご覧のとおり、テキストは、元に戻すとうまくいきません。
  • $、 、 などの%文字は&左右対称ではありませんが、特殊な Unicode ブロックを使用しない限り、反対の文字がない場合もあります。
于 2012-10-28T22:34:28.980 に答える
0

各行が同じ長さになるように、各行を列幅に固定する必要があるかもしれません。したがって、最初の行が文字の後にLFが続く場合は、反転する前に、反転に空白を埋め込む必要があります。

于 2012-10-28T22:00:25.580 に答える