如何使用awk交换线路,只需一次通过,并且内存使用有限?

如何使用awk交换线路,只需一次通过,并且内存使用有限?,awk,swap,gawk,Awk,Swap,Gawk,在前一篇文章中,给出了这样的答案:,虽然很漂亮,但问题是您应该读取输入文件两次 我希望制作一个GNUAWK脚本,以便只读取一次输入 cat swap_line.awk 你得到 BEGIN { if(init > end){ exit 1; } flag = 1; memory_init = ""; memory = "" } { if (NR != init && NR != end){ if(flag==1){ pri

在前一篇文章中,给出了这样的答案:,虽然很漂亮,但问题是您应该读取输入文件两次

我希望制作一个GNUAWK脚本,以便只读取一次输入

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万行之间进行交换,我将999998行存储在
memory
变量@JoseRicardoBustosM中。在awk中,如果不将init中的行保存到内存中结束行之前的一行,就不可能一次完成。想想看,在你已经读到的内容之前,不可能有一行N行奇迹般地出现在当前行的位置。最好的解决方案无疑是一种简单的两遍方法,第一遍保存行,第二遍使用行。我将包括所有涉及提前grep或在“2”pass进近桶中使用getline循环的解决方案

FWIW以下是我的实际操作方法(这是一种2通方法):

请注意,如果您对如何处理超过文件末尾的“end”没有非常具体的要求,那么解决方案就是:

$ 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\u line.awk

NR==init{hd=$0;而((getline0&&++c@JoseRicardoBustosM。在awk中,如果不将init中的行保存到内存中的结束行之前的一行,就不可能一次完成这项工作。只要想想看,不可能在已经阅读的内容之前获得一行N行,从而奇迹般地显示在当前行的位置上。解决这一问题的最佳方法是确定的这是一个简单的两遍方法,在第一遍保存行并在第二遍使用它们。我包括了所有涉及提前grep或在“两遍”方法桶中使用getline循环的解决方案

FWIW以下是我的实际操作方法(这是一种2通方法):

请注意,如果您对如何处理超过文件末尾的“end”没有非常具体的要求,那么解决方案就是:

$ 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\u line.awk

NR==init{hd=$0;while((getline0&&++c我认为您工作太辛苦了。这不会试图处理极端情况(例如,如果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
$cat swap.sh
#!/bin/sh
陷阱'rm-f$T1'0
T1=$(mktemp)
awk'

NR我认为您工作太辛苦了。这并不能尝试处理极端情况(例如,如果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
$cat swap.sh
#!/bin/sh
陷阱'rm-f$T1'0
T1=$(mktemp)
awk'

NR我同意需要两次通过。第一次通过可以使用专门为任务设计的工具:

# $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

我同意需要两个过程。第一个过程可以使用专门为任务设计的工具完成:

# $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

如果您只接受一个用awk编写的解决方案,那么为什么这个标签是bash?(如果您接受一个用纯bash编写的没有awk的解决方案,那么可能需要更改标题)。也就是说,除非您可以保证两行的长度相同(或者,算法复杂度要高得多,可以保证原始文件中后面的行比开头的行短),如果没有某种外部存储,您所要求的几乎是不可能的。这可能只是简单地将临时文件写入磁盘,或将输出写入与输入不同的文件,但内容需要存储在某个位置。解决方案当然可以使用磁盘而不是内存。不过,这可能更适合我如果您对awk/惯用语的一般改进感兴趣。@hek2mgl,我认为这大致相当于我对dd的建议,尽管使用内存而不是在磁盘上四处寻找。:)@JoseRicardoBustosM.,问题是,在文件系统级别,您只能在中查找一定数量的字节,而不能在中查找一定数量的行。要计算出多少字节与一定数量的行相对应,您需要从一开始就读取。如果您只接受用awk编写的解决方案,为什么这个标记为bash?(如果您接受纯bash的解决方案,而不使用awk,那么可能需要更改标题)。也就是说,除非您可以保证这两行的长度相同(或者,由于算法复杂度大大提高,可以保证原始文件中后面的行比开头的行短),如果没有某种外部存储,您所要求的几乎是不可能的。这可能只是简单地将临时文件写入磁盘,或将输出写入与输入不同的文件,但内容需要存储在某个位置。解决方案当然可以使用磁盘而不是内存。不过,这可能更适合我溜溜球