列名がCSVファイルの特定のパターンに一致する列全体を抽出する方法は?

列名がCSVファイルの特定のパターンに一致する列全体を抽出する方法は?

私はUnixに慣れていない非常に大きなCSVファイルを扱っています。

例は次のとおりです。

ABC1,ABC2,ABC3,DDD,EEE,FFF
1,2,3,4,5,6
1,2,3,4,5,6

で始まるすべての列を抽出する方法はABC

答え1

次のawkプログラムはそのトリックを実行します。次のファイルに保存しますextract.awk

#!/bin/awk -f

BEGIN { FS=OFS=","}

FNR==1 {
  for (i=1;i<=NF;i++) {
    if (index($i,startstr)==1) cols[++ncol]=i;
  }
}

{ for (j=1;j<=ncol;j++) printf("%s%s",$(cols[j]),j==ncol?ORS:OFS) }

それから電話してください。

~$ awk -f extract.awk -v startstr="ABC" input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

文字列を検索する変数の位置を定義しますstartstr

これにより、まず入力フィールドと出力フィールドの区切り文字がに設定されます,

  • 最初の行(ヘッダー行)は、変数に格納されている検索文字列で始まる列名があることを確認しますstartstr。その場合、列番号がcols「印刷する列」配列に追加されます。
  • 各行(最初の行を含む)に対して保存されているすべての列の値を印刷し、最後の列のcols場合は、フィールド区切り文字またはレコード区切り文字(デフォルトは改行)を印刷します。

実際の検索文字列に正規表現コンテキストに特殊文字が含まれている場合、使用する関数は正規表現ベースの一致ではindex()なく文字通りの文字列一致を実行します。awk正規表現の基本検索を使用する必要がある場合は、以下を変更してください。

if (index($i,startstr)==1) cols[++ncol]=i;

到着

if ($i ~ startstr) cols[++ncol]=i

ただし、その中のすべての文字はstartstr正規表現トークンとして解釈されるため、注意しないと予期しない動作が発生する可能性があります。言及した例startstrの場合^ABC

答え2

awkを使用してこれを行うことはできますが、Perlのアレイスライス機能のおかげでPerlでは簡単です。 awkでは、同じ結果を得るために必要な配列を繰り返す必要があります。

#!/usr/bin/perl

use strict;
my @wanted;   # array to hold the indices we want to print

while(<>) {
  chomp;

  # split the input line into array @F, using commas as the delimiter.
  my @F = split /,/;

  if ($. == 1) {  # process the first line (the headers)
    # if a header matches the regex, add it to @wanted
    foreach my $i (0 .. $#F) {
      push @wanted, $i if $F[$i] =~ m/^abc/i;
    };
  };

  # print the columns of @F whose indices are listed in @wanted
  print join(",", @F[@wanted]), "\n";
}

たとえば、別の名前で保存しabc.plて実行可能にしchmod +x abc.plたら、次のように実行します。

$ ./abc.pl input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

動作原理:

  • ループは、各フィールドのforeach一致するインデックス番号(大文字と小文字を区別しない)を配列に追加します。/abc/@wanted
  • 与えられたサンプル入力の後には、最終的に、とが@wanted含まれます。012
  • @F[@wanted]ステートメントで使用されるasは実際にはprint join()(つまり、要素とof)と同じです。これらの要素はコンマ文字で連結して印刷されます。@F[0,1,2]012@F

追加:

if ($. == 1) {...}foreachPerlの機能を使用するためにusingブロックを書き換えることができますgrep。ブロック全体を1行に置き換えることができます。

   @wanted = grep($F[$_] =~ m/^abc/i, keys @F) if ($. == 1);

一部の人々はこれがPerl寛容であると言うでしょう。私は同意しません。 Perlにはforeachand grep(およびmap配列joinやリストを扱う他の多くの関数と演算子)があり、次のものを使用します。どのその中には「慣用的なパール」があります。

注:keysインデックス配列を使用するには、2010年にリリースされたv5.12以降のPerlバージョンが必要です。以前はkeysハッシュ配列だけで作業していました。

さらに、スクリプト全体は、次の2つのステートメントのみを使用して1行に圧縮できます。

$ perl -F, -lne '@wanted = grep($F[$_] =~ m/^abc/i, keys @F) if ($. == 1);
                 print join(",", @F[@wanted]);' input.csv

答え3

使いやすいです。ミラー,WHO切る正規表現一致列名のオプションがあります。

$ mlr --csv cut -r -f '^ABC' input.csv
ABC1,ABC2,ABC3
1,2,3
1,2,3

答え4

flds=$(< file head -n 1 | tr ',' '\n' | grep -ne '^ABC' | cut -d: -f1 | paste -sd, -)

cut -d, -f"${flds}" file

ABC1,ABC2,ABC3
1,2,3
1,2,3

これを2つのステップで行います。まず、ヘッダーを抽出してから、ヘッダーからABCで始まるフィールドのフィールド番号を取得します。

次に、この情報を使用してcutコマンドに接続し、ファイル全体からこれらのフィールドを抽出します。

関連情報