How to horizontally reflect ascii art?

So ... I know that I can change the order of the lines in a file using tac or several other tools, but how to change the order in another dimension, i.e. horizontally? I am trying to do this with the following awk script:

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

This seems to appeal to the characters, but it is distorted, and I don't understand why. What am I missing?

I do this in awk, but is there something better? sed , maybe?

Here is an example. The input is as follows:

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

And the result is as follows:

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

Note that the output is identical whether I use rev or my own awk script. As you can see, things have been canceled, but ... it's crippled.

+6
source share
6 answers

rev nice, but it doesn't enter any input lines. It just cancels them.

The "manipulation" you see is that one line can contain 20 characters, and the next length can be 15 characters. In the input text they have a left column. But in your output text, they need to split the right column.

So you need to fill out. Ah, and an asymmetric rotation, as Joachim said.

Here is my 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]); } } 

(I did this in gawk , I don't think I used any gawkisms, but if you are using the more classic version of awk , you might need to adjust this.)

Use it the same way you use rev.

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

If you are configured for this, you can even run the translation from the awk script by adding it to the last line of printf :

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

But I do not recommend it, as the revawk command revawk less useful for other applications.

+7
source

Here is one way: GNU awk and rev

Run as:

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

The content of script.awk :

 FNR==NR { if (length > max) { max = length } next } { while (length < max) { $0=$0 OFS } }1 

Alternatively, here is a single line:

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

Results:

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

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

Here's another way to use GNU awk :

Run as:

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

The content of 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 } } 

Alternatively, here is a single line:

 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){,} 

Results:

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

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

Explanation:

Here is an explanation of the second answer. I take basic knowledge of 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 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. 

Some other things you might need:

The wildcard {,} is an abbreviation for repeating the input file name twice. Unfortunately, this is not a standard Bourne shell. However, you can use instead:

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

In addition, in the first example, { ... }1 is short for { ... print $0 }

NTN.

+5
source

Your lines do not have the same length, so reversing the cow will break it. What you need to do is "superimpose" the lines one length and then the other.

For instance:

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

will move it to 40 columns and then undo.

EDIT: @ghoti made a script that surely beats this simplified converse, take a look at its answer.

+4
source

You can also do this with bash, coreutils and sed (in order to make it work with zsh, the while loop should be wrapped in tr ' ' '\x01' | while ... | tr '\x01' ' ' , I don’t know why ):

 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 '[[]()<>/\\]' '[][)(><\\/]' 

Conclusion:

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

This leaves a lot of extra spaces at the end, add | sed 's/ *$//' | sed 's/ *$//' to delete.

Explanation

The barn output is output, especially the backslashes that sed takes care of, duplicating them. To get the correct line width printf '%*s' len str , len used as a parameter for the line length. Finally, asymmetric characters are replaced by their peers, as is done in Gotti's answer.

+2
source

I don't know if you can do this in AWK, but here are the necessary steps:

Determine the length of your original longest line, you need it to give the correct spacing for any smaller lines.

  (__)\ )\/\ 

For the last char on each line, specify the need for startup spaces based on what you purchased from the first step.

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

For each line, apply the required number of spaces, and then the original characters in reverse order.

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

Finally, replace all characters that are not horizontally symmetrical with their horizontally opposite characters. (from < to > , [ to ] , etc.)

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

Two things to watch out for:

  • The text, as you can see, will not change with reverses.
  • Characters like $ , % and & are not horizontally symmetrical, but may also not have the opposite if you are not using specialized Unicode Blocks.
+1
source

I would say that you may need each row for a fixed column width so that each row has the same length. Therefore, if the first line is a character followed by LF, you will need to reverse the space with a space before reverse.

0
source

All Articles