在之前的帖子中,显示了这个答案:answer user2138595,虽然很漂亮,但问题是你应该两次读取输入文件。
我希望制作一个GNU awk脚本只读一次输入。
cat swap_line.awk
你得到了
BEGIN {
if(init > end){
exit 1;
}
flag = 1;
memory_init = "";
memory = ""
}
{
if (NR != init && NR != end){
if(flag==1){
print $0;
}else{
memory = memory""$0"\n";
}
}else if(end == init){
print $0;
}else if(NR == init){
flag = 0;
memory_init = $0;
}else{
#NR == end
print $0;
printf("%s",memory);
print memory_init;
flag = 1;
}
}
END {
#if end is greater than the number of lines of the file
if(flag == 0){
printf("%s",memory);
print memory_init;
}
}
脚本运行良好
cat input
1
2
3
4
5
awk -v init=2 -v end=4 -f swap_line.awk input
1
4
3
2
5
awk -v init=2 -v end=2 -f swap_line.awk input
1
2
3
4
5
awk -v init=2 -v end=8 -f swap_line.awk input
1
3
4
5
2
问题
我怎么能以更好的方式制作剧本?因为,我不喜欢使用memory
变量,因为对于大文件可能有问题,例如如果输入文件是1000万行并且想要在第1行和第1000行之间进行交换,我存储memory
变量
答案 0 :(得分:2)
@JoseRicardoBustosM。如果不将init中的行保存到内存中的结束行之前,则无法在awk的一次传递中执行此操作。试想一下,在你已经阅读过的奇迹般地出现代替当前行的情况下,不可能获得N行。对此最好的解决方案绝对是一个简单的2遍方法,即在第一遍中保存线并在第二轮中使用它们。我将所有涉及grep-ing的解决方案或在" 2" -pass方法桶中使用getline循环包括在内。
FWIW就像我真正做到的那样(这是一种2遍方式):
$ cat swap_line.awk
BEGIN { ARGV[ARGC]=ARGV[ARGC-1]; ARGC++ }
NR==FNR { if (NR==end) tl=$0; next }
FNR==init { hd=$0; $0=tl; nr=NR-FNR; if (nr<end) next }
FNR==end { $0=hd }
FNR==nr { if (nr<end) $0 = $0 ORS hd }
{ print }
$ awk -v init=2 -v end=4 -f swap_line.awk input
1
4
3
2
5
$ awk -v init=2 -v end=2 -f swap_line.awk input
1
2
3
4
5
$ awk -v init=2 -v end=8 -f swap_line.awk input
1
3
4
5
2
请注意,如果您对如何处理&#34; end&#34;没有特别要求。超过文件末尾的解决方案就是:
$ cat swap_line.awk
BEGIN { ARGV[ARGC]=ARGV[ARGC-1]; ARGC++ }
NR==FNR { if (NR==end) tl=$0; next }
FNR==init { hd=$0; $0=tl }
FNR==end { $0=hd }
{ print }
如果你真的想要考虑一下(再次,只是为了阳光灿烂的日子):
$ cat swap_line.awk
NR==init { hd=$0; while ((getline<FILENAME)>0 && ++c<end); }
NR==end { $0=hd }
{ print }
$ awk -v init=2 -v end=4 -f swap_line.awk input
1
4
3
2
5
我仍然认为最后一个作为&#34; 2&#34; -pass方法,如果我没有完全理解{{3}中列出的所有警告,我就不会这样做}。
答案 1 :(得分:2)
我认为你工作太辛苦了。这没有尝试处理极端情况(例如,如果end大于行数,则不会打印初始行,但可以在END块中轻松处理),因为我认为处理边缘情况模糊不清这个想法。即,打印直到到达要换出的行,然后将数据存储在文件中,然后打印行交换,存储的数据和初始行,然后打印文件的其余部分:
$ cat swap.sh
#!/bin/sh
trap 'rm -f $T1' 0
T1=$(mktemp)
awk '
NR<init { print; next; }
NR==init { f = $0; next; }
NR<end { print > t1; next; }
NR==end { print; system("cat "t1); print f; next; }
1
' init=${1?} end=${2?} t1=$T1
$ yes | sed 10q | nl -ba | ./swap.sh 4 8
1 y
2 y
3 y
8 y
5 y
6 y
7 y
4 y
9 y
10 y
答案 2 :(得分:2)
我同意需要2次传球。第一遍可以使用专门为任务设计的工具完成:
# $init and $end have been defined
endline=$( tail -n "+$end" file | head -n 1 )
awk -v init="$init" -v end="$end" -v endline="$endline" '
NR == init {saved = $0; $0 = endline}
NR == end {$0 = saved}
{print}
' file
在函数中隐藏细节:
swap_lines () {
awk -v init="$1" \
-v end="$2" \
-v endline="$(tail -n "+$2" "$3" | head -n 1)" \
'
NR == init {saved = $0; $0 = endline}
NR == end {$0 = saved}
1
' "$3"
}
seq 5 > file
swap_lines 2 4 file
1
4
3
2
5