Unix - 文件中行和列的循环移位

时间:2017-01-31 10:13:51

标签: unix awk

给定一个包含以下内容的文件:

1 2 3 4
5 6 7 8
a b c d
e f g h

是否有任何unix命令可用于循环移动行和列?

我正在寻找类似的话,

circular_shift -r 2 <file>(换乘2行)给出:

a b c d
e f g h
1 2 3 4
5 6 7 8

circular_shift -c 2 <file>(按2移位列)给出:

3 4 1 2
7 8 5 6
c d a b
g h e f

谢谢!

5 个答案:

答案 0 :(得分:6)

使用awk进行行移位处理file两次:

$ awk -v r=2 'NR==FNR && FNR>r || NR>FNR && FNR<=r' file file
a b c d
e f g h
1 2 3 4
5 6 7 8

基本上它会在第一次打印时记录NR > r,在第二次打印时记录NR <= r

修改:有关记录和字段的版本:

$ awk -v r=1 -v c=1 '
NR==FNR && FNR>r || NR>FNR && FNR<=r {
    j=0;
    for(i=c+1;++j<=NF;i=(i<NF?i+1:1)){
        printf "%s%s",$i,(i==c?ORS:OFS)
    }
}
' foo foo
6 7 8 5
b c d a
f g h e
2 3 4 1

(我在参加会议时几乎未经测试......它至少在c=0失败了)

答案 1 :(得分:4)

gawk

中使用多维数组的另一种解决方案
  

circular_shift.awk

{for(i=1; i<=NF; ++i){d[NR][i]=$i}}
END{
    c=c%NF; r=r%NR
    for(i=1; i<=NR; ++i){
        nr = i + (i>r?0:NR) - r
        for(j=1; j<=NF; ++j){
            nc = j + (j>c?0:NF) - c
            printf d[nr][nc] (j!=NF?OFS:RS)
        }
    }
}
  

awk -vr = 2 -f circular_shift.awk file

a b c d
e f g h
1 2 3 4
5 6 7 8
  

awk -vc = 2 -f circular_shift.awk file

3 4 1 2
7 8 5 6
c d a b
g h e f
  

awk -vr = 2 -vc = 2 -f circular_shift.awk file

c d a b
g h e f
3 4 1 2
7 8 5 6

答案 2 :(得分:3)

换档行

您可以使用headtail和shell:

function circular_shift() {
    n=$1
    file=$2
    tail -n +"$((n+1))" "$file"
    head -n "$n" "$file"
}

像这样调用函数:

circular_shift 2 <file>

一个限制。上述功能仅适用于n <= nlines(file)。如果你想摆脱这个限制,你需要事先知道文件的长度并使用模运算符:

function circular_shift() {
    n=$1
    file=$2
    len="$(wc -l "$file"|cut -d" " -f1)"
    n=$((n%len))
    tail -n +"$((n+1))" "$file"
    head -n "$n" "$file"
}

现在尝试致电:

circular_shift 6 <file>

移动列

对于列移位,我会使用awk

列shift.awk

{
    n = n % NF
    c = 1

    for(i=NF-n+1; i<=NF; i++) {
        a[c++] = $i
    }

    for(i=1; i<NF-n+1; i++) {
        a[c++] = $i
    }

    for(i=1; i<c; i++) {
        $i = a[i]
    }
}

print

将它包装在shell函数中:

function column_shift() {
    n="$1"
    file="$2"
    awk -v n="$n" -f column-shift.awk "$file"
}

答案 3 :(得分:0)

@Vivek V K,试试: 用于将行移动到向上的数字。

awk -vcount=2 'NR>count{print;next} NR<=count{Q=Q?Q ORS $0:$0} END{print Q}'   Input_file

如果要移动字段,请尝试以下操作:

awk -vcount=2 '{for(i=count+1;i<=NF;i++){Q=Q?Q FS $i:$i};for(j=1;j<=count;j++){P=P?P FS $j:$j};print Q FS P;Q=P=""}'   Input_file

答案 4 :(得分:0)

awk -v C=$1 -v R=$2 '
function PrintReverse () {
      if( ! R ) return

      for( i=1; i>=0; i--) {
         for( j=1; j<=R; j++) {
            #print "DEBUG:: i: "i " j:" j " i * R + j :" i * R + j  " lr:" lr
            print L[ i * R + j ]
            L[ i * R + j ] = ""
         }
      }
   }

   {
   if( C ) {
      # Reverse Column
      for ( i=1; i<=NF; i+=2*C) {
         for( j=0; j<C; j++) {
            #print "DEBUG:: i: "i " j:" j " NF:" NF
            tmp = $(i+j)
            $(i+j) = $(i+j+C)
            $(i+j+C) = tmp
            }
         }
      $1=$1
      }

   if ( R ) {
      # Line buffer
      lr = ( FNR - 1 ) % ( R * 2 ) + 1
      L[ lr] = $0
      }
    else print
   }

   lr >= ( R * 2) { PrintReverse() }

   END { if( lr < ( R * 2 )) PrintReverse() }
   ' YourFile

将做你的反向行动

  • R是行数,C是要反转的列数。
  • 使用2个循环(在另一个循环中有1个循环)[不是最快但是更明确地理解这个概念离子]
  • 这是行的缓冲区置换,通过在行数为两倍的缓冲区中加载行,以相反的顺序打印2个半内容
  • 这是列置换的字段交换,它循环2 *列交换字段内容的数量与字段索引+列数
  • 在缓冲区被供给之后处理行(实际上每个R * 2行)
  • 列在每行处理
  • 我添加了一个测试( C )( ! R ),...以允许单一反向(仅限行或列)