所以......我知道我可以使用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脚本,输出都是相同的。正如你所看到的,事情是相反的,但......它已经被破坏了。
答案 0 :(得分:7)
rev
很不错,但它不会填充输入行。它只是逆转了它们。
你看到的“破坏”是因为一行可能是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
执行此操作;我认为我没有使用任何gawkisms,但如果您使用的是更经典的awk
变体,则可能需要对此进行调整。)
使用此方法与使用rev。
相同ghoti@pc:~$ echo hello | cowsay | ./revawk | tr '[[]()<>/\\]' '[][)(><\\/]'
_______
< olleh >
-------
^__^ /
_______/(oo) /
/\/( /(__)
| w----||
|| ||
如果您愿意这样做,您甚至可以通过将其添加到上一个printf
行来从awk脚本中运行翻译:
printf(fmt," ",o[i]) | "tr '[[]()<>/\\]' '[][)(><\\/]'";
但我不推荐它,因为它使revawk
命令对其他应用程序不那么有用。
答案 1 :(得分: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。
答案 2 :(得分:4)
你的线长度不一样,所以逆转牛会破坏它。你需要做的是将线“填充”到相同的长度,然后反转。
例如;
cowsay <<<"hello" | awk '{printf "%-40s\n", $0}' | rev
将其填充到40列,然后反转。
编辑:@ghoti做了一个肯定胜过这种简单反转的剧本,看看他的回答。答案 3 :(得分: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输出,尤其是通过复制它们来处理的反斜杠。要使用正确的行宽printf '%*s' len str
,使用len
作为字符串长度参数。最后,非对称字符被其对应字符替换,就像在 ghoti的答案中所做的那样。
答案 4 :(得分: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块。答案 5 :(得分:0)
我会说你可能需要每一行都是固定的列宽,所以每一行都是相同的长度。因此,如果第一行是后跟LF的字符,则需要在反转之前用空格填充反向。