簡體   English   中英

如何使用 awk 重新排列列?

[英]How to rearrange the columns using awk?

我有一個包含 120 列的文件。 它的一部分在這里有 12 列。

A1      B1     C1      D1       A2      B2     C2      D2       A3      B3      C3      D3     
4       4       5       2       3       3       2       1       9       17      25      33
5       6       4       6       8       2       3       5       3       1       -1      -3
7       8       3       10      13      1       4       9       -3      -15     -27     -39
9       10      2       14      18      0       5       13      -9      -31     -53     -75
11      12      1       18      23      -1      6       17      -15     -47     -79     -111
13      14      0       22      28      -2      7       21      -21     -63     -105    -147
15      16      -1      26      33      -3      8       25      -27     -79     -131    -183
17      18      -2      30      38      -4      9       29      -33     -95     -157    -219
19      20      -3      34      43      -5      10      33      -39     -111    -183    -255
21      22      -4      38      48      -6      11      37      -45     -127    -209    -291

我想通過將所有 A 列(A1 A2 A3 A4)和類似的所有 B(B1 B2 B3 B4)、Cs(C1 C2 C3 C4)、Ds(D1 D2 D3 D4)放在一起來重新排列它。

我希望將列打印為

A1 A2 A3 A4 B1 B2 B3 B4 C1 C2 C3 C4 D1 D2 D3 D4
 

我的腳本是:

#!/bin/sh
sed -i '1d' input.txt
for i in {1..4};do
    j=$(( 1 + $(( 3 * $((  i - 1 )) ))  ))
awk '{print $'$j'}' input.txt >> output.txt
done
for i in {1..4};do
    j=$(( 2 + $(( 3 * $((  i - 1 )) ))  ))
awk '{print $'$j'}' input.txt >> output.txt
done
for i in {1..4};do
    j=$(( 3 + $(( 3 * $((  i - 1 )) ))  ))
awk '{print $'$j'}' input.txt >> output.txt
done

它在一列中打印所有內容。

是否只是 A,B,C,D,A,B,C,D 一路穿過? 像這樣的東西應該工作:

awk '{
    for (i=0; i<4; ++i) {  # i=0:A, i=1:B,etc.
       for (j=0; 4*j+i<NF; ++j) {
         printf "%s%s", $(4*j+i+1), OFS;
       }
    }
    print ""
}'

這是兩個通用方法解決方案,無需對 Input_file 中的字段編號進行硬編碼,值可以按任何順序排列,並且會自動對它們進行排序。 使用 GNU awk編寫和測試,並帶有示例。

第一種解決方案:遍歷所有行及其各自的字段,然后按值排序以對標題執行索引。

awk '
FNR==1{
  for(i=1;i<=NF;i++){
     arrInd[i]=$i
  }
  next
}
{
  for(i=1;i<=NF;i++){
     value[FNR,arrInd[i]]=$i
  }
}
END{
  PROCINFO["sorted_in"]="@val_num_asc"
  for(i in arrInd){
     printf("%s%s",arrInd[i],i==length(arrInd)?ORS:OFS)
  }
  for(i=2;i<=FNR;i++){
     for(k in arrInd){
        printf("%s%s",value[i,arrInd[k]],k==length(arrInd)?ORS:OFS)
     }
  }
}
'   Input_file

或者,如果您想以表格格式獲取 output,則在上述解決方案中進行小調整。

awk '
BEGIN { OFS="\t" }
FNR==1{
  for(i=1;i<=NF;i++){
    arrInd[i]=$i
  }
  next
}
{
  for(i=1;i<=NF;i++){
    value[FNR,arrInd[i]]=$i
  }
}
END{
  PROCINFO["sorted_in"]="@val_num_asc"
  for(i in arrInd){
    printf("%s%s",arrInd[i],i==length(arrInd)?ORS:OFS)
  }
  for(i=2;i<=FNR;i++){
    for(k in arrInd){
       printf("%s%s",value[i,arrInd[k]],k==length(arrInd)?ORS:OFS)
    }
  }
}
' Input_file | column -t -s $'\t'


第二種解決方案:與第一種解決方案幾乎相同的概念,這里遍歷條件內的數組,而不是在該程序的END塊中顯式調用它。

awk '
BEGIN { OFS="\t" }
FNR==1{
  for(i=1;i<=NF;i++){
    arrInd[i]=$i
  }
  next
}
{
  for(i=1;i<=NF;i++){
    value[FNR,arrInd[i]]=$i
  }
}
END{
  PROCINFO["sorted_in"]="@val_num_asc"
  for(i=1;i<=FNR;i++){
    if(i==1){
       for(k in arrInd){
          printf("%s%s",arrInd[k],k==length(arrInd)?ORS:OFS)
       }
    }
    else{
       for(k in arrInd){
          printf("%s%s",value[i,arrInd[k]],k==length(arrInd)?ORS:OFS)
       }
    }
  }
}
' Input_file | column -t -s $'\t'

與@MarkReed 類似的方法是操縱增量而不是測試條件,可以寫成:

awk '{
  for (n=1; n<=4; n++)
    for (c=n; c<=NF; c+=4)
      printf "%s%s", ((c>1)?"\t":""), $c
    print ""
  }
' cols.txt

示例使用/輸出

使用cols.txt中的示例輸入,您將擁有:

$ awk '{
>   for (n=1; n<=4; n++)
>     for (c=n; c<=NF; c+=4)
>       printf "%s%s", ((c>1)?"\t":""), $c
>     print ""
>   }
> ' cols.txt
A1      A2      A3      B1      B2      B3      C1      C2      C3      D1      D2      D3
4       3       9       4       3       17      5       2       25      2       1       33
5       8       3       6       2       1       4       3       -1      6       5       -3
7       13      -3      8       1       -15     3       4       -27     10      9       -39
9       18      -9      10      0       -31     2       5       -53     14      13      -75
11      23      -15     12      -1      -47     1       6       -79     18      17      -111
13      28      -21     14      -2      -63     0       7       -105    22      21      -147
15      33      -27     16      -3      -79     -1      8       -131    26      25      -183
17      38      -33     18      -4      -95     -2      9       -157    30      29      -219
19      43      -39     20      -5      -111    -3      10      -183    34      33      -255
21      48      -45     22      -6      -127    -4      11      -209    38      37      -291

這是一個簡潔的通用解決方案,它不受內存限制,就像 RavinderSing13 的解決方案一樣。 (也就是說,它不會將整個輸入存儲在一個數組中以便在 END 中打印。)


BEGIN {
   OFS="\t" # output field separator
}

NR==1 {
   # Sort column titles
   for (i=1;i<=NF;i++) { sorted[i]=$i; position[$i]=i }
   asort(sorted)
   # And print them
   for (i=1;i<=NF;i++) { $i=sorted[i] }
   print
   next
}

{
   # Make an array of our input line...
   split($0,line)
   for (i=1;i<=NF;i++) { $i=line[position[sorted[i]]] }
   print
}

這里的想法是,在輸入的第一行,我們記錄輸入中列的 position,然后使用asort()對列名列表進行排序。 重要的是,列名不能重復,因為它們被用作數組的索引。

當我們逐步瀏覽數據時,通過將每個字段替換為 position 中的值(按第一行排序)來重新排序每一行。

正確設置輸入字段分隔符(空格、制表符、逗號等)並在每行中包含完整的字段集非常重要,否則 output 將出現亂碼。

此外,這不會創建列。 您在問題中提到了 A4,但您的示例數據中沒有 A4。 我們只對現有的進行排序。

最后,由於使用了asort() ,這是一個GNU awk程序。

Using any awk for any number of tags (non-numeric leading strings in the header line) and/or numbers associated with them in the header line, including different counts of each letter so you could have A1 A2 but then B1 B2 B3 B4,重現 output 中的輸入順序,並在 memory 中一次僅存儲 1 行:

$ cat tst.awk
BEGIN { OFS="\t" }
NR == 1 {
    for ( fldNr=1; fldNr<=NF; fldNr++ ) {
        tag = $fldNr
        sub(/[0-9]+$/,"",tag)
        if ( !seen[tag]++ ) {
            tags[++numTags] = tag
        }
        fldNrs[tag,++numTagCols[tag]] = fldNr
    }
}
{
    out = ""
    for ( tagNr=1; tagNr<=numTags; tagNr++ ) {
        tag = tags[tagNr]
        for ( tagColNr=1; tagColNr<=numTagCols[tag]; tagColNr++ ) {
            fldNr = fldNrs[tag,tagColNr]
            out = (out=="" ? "" : out OFS) $fldNr
        }
    }
    print out
}

$ awk -f tst.awk file
A1      A2      A3      B1      B2      B3      C1      C2      C3      D1      D2      D3
4       3       9       4       3       17      5       2       25      2       1       33
5       8       3       6       2       1       4       3       -1      6       5       -3
7       13      -3      8       1       -15     3       4       -27     10      9       -39
9       18      -9      10      0       -31     2       5       -53     14      13      -75
11      23      -15     12      -1      -47     1       6       -79     18      17      -111
13      28      -21     14      -2      -63     0       7       -105    22      21      -147
15      33      -27     16      -3      -79     -1      8       -131    26      25      -183
17      38      -33     18      -4      -95     -2      9       -157    30      29      -219
19      43      -39     20      -5      -111    -3      10      -183    34      33      -255
21      48      -45     22      -6      -127    -4      11      -209    38      37      -291

或使用不同格式的標簽和每個標簽的不同列數:

$ cat file
foo1    bar1    bar2    bar3    foo2    bar4
4       4       5       2       3       3
5       6       4       6       8       2

$ awk -f tst.awk file
foo1    foo2    bar1    bar2    bar3    bar4
4       3       4       5       2       3
5       8       6       4       6       2

以上假設您希望每個標簽的 output 順序與輸入順序相匹配,而不是基於每個標簽后的數值,因此如果您輸入了A2 B1 A1那么 output 將是A2 A1 B1 ,而不是A1 A2 B1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM