30397


以下のような、ヘッダ付き csv から、ヘッダを複数個、指定して、その列のみを出力したいと考えました。また、これを実現するにあたり、どこにでもある awk
で実装したいと考えました。

csv 例:

[code]

    NAME,AGE,GENDER
    Jack,30,Male
    Joe,25,Male
    Mary,23,Female
    
[/code]

実装したい関数の例:

[code]

    cat the_csv | extract_columns AGE GENDER
    # => AGE, GENDER の列だけが出力される。つまり:
    # 30,Male
    # 25,Male
    # 23,Female
    
[/code]

これは、どうやったら実現できますでしょうか。


CC BY-SA 3.0
2016-11-14T06:53:47.667
30390
2019-10-08T07:40:18.333
754
question
awk
awk で、ヘッダ付き csv から、 ヘッダでカラムたちを抜き出すには？


gawk, nawk, mawk での動作を確認していますが、mawk の `length` 関数は配列には対応していませんので `arr_length`
というユーザ定義関数を追加しています。

**extract_columns**

[code]

    #!/usr/bin/awk -f
    
    # mawk doesn't have the function that returns the length of an array
    function arr_length(arr,    i) {
      i = 0;for (_ in arr) i++;return i
    }
    
    BEGIN {
      if (ARGC < 2) {
        print "No query strings." | "cat 1>&2"
        exit(1)
      }
    
      FS = OFS = ","
    
      # read query string
      for (i=1;i<ARGC;i++) {
        col[ARGV[i]] = ""
      }
    
      # read header
      getline < "-"
      for (i=1;i<=NF;i++) {
        if ($i in col){
          ix[i] = ""
        }
      }
      if (arr_length(ix) == 0) {
        print "No matching columns." | "cat 1>&2"
        exit(1)
      }
    
      # query data
      while (getline r < "-") {
        split(r, ar)
        for (i=a=1;i<=arr_length(ar);i++) {
          if (i in ix) {
            $(a++) = ar[i]
          }
        }
        NF = a - 1; print
      }
    }
    
[/code]

* * *
[code]

    $ cat the_csv | ./extract_columns AGE GENDER
    30,Male
    25,Male
    23,Female
    
[/code]


CC BY-SA 3.0
2016-11-14T09:45:37.083
30395
2016-11-14T09:45:37.083
30390
answer


印刷する列を抽出し、それらをループすることができます。

[code]

    BEGIN {FS=OFS=","; split(cols, names)}
    FNR==1 {
       for(i=1;i<=NF;i++) {          # extract column numbers to be printed
              for (n in names) {
                  if (names[n]==$i)
                      columns[i]=1
              }
       }
    next
    }
    {for (c in columns)              # loop through columns
        st=(st ? st FS : "") $c      # pick needed to print
     print st                        # print line
     st=""
    }
    
[/code]

### 例

[code]

    $ awk -v cols="AGE,NAME" -f script.wk file
    Jack,30
    Joe,25
    Mary,23
    $ awk -v cols="AGE,GENDER" -f script.wk file
    30,Male
    25,Male
    23,Female
    
[/code]


CC BY-SA 4.0
2016-11-14T11:10:34.010
30397
2019-10-08T07:40:18.333
2019-10-08T07:40:18.333
19559
19559
30390
answer