列を追加し、列全体にファイル名(「.csv」なし)を入力します - BaSH 内の複数のファイルの場合

列を追加し、列全体にファイル名(「.csv」なし)を入力します - BaSH 内の複数のファイルの場合

注意: まったくの初心者です。.csv ファイルに列を追加する必要があります。列ヘッダーは「名前」にできますが、列全体はまったく同じである必要があります (ファイル自体の名前、 ) filename。現在、各ファイルには 3 つの変数しかありませんが、行は 2100 行あります。

例: ファイルの場合「bcc1_45Fall_10010002.csv」これは私が持っているものです -

   HUC8       YEAR    RO_MM
   10010002   1961    74.7
   10010002   1962    69.1
   10010002   1963    52.0
   10010002   1964   130.7
   10010002   1965    32.2
   10010002   1966    85.4

これが私が望んでいるもの -

  NAME                   HUC8       YEAR    RO_MM
  bcc1_45Fall_10010002   10010002   1961    74.7
  bcc1_45Fall_10010002   10010002   1962    69.1
  bcc1_45Fall_10010002   10010002   1963    52.0
  bcc1_45Fall_10010002   10010002   1964   130.7
  bcc1_45Fall_10010002   10010002   1965    32.2
  bcc1_45Fall_10010002   10010002   1966    85.4

またはこれ -

  HUC8                    YEAR    RO_MM
  bcc1_45Fall_10010002    1961    74.7
  bcc1_45Fall_10010002    1962    69.1
  bcc1_45Fall_10010002    1963    52.0
  bcc1_45Fall_10010002    1964   130.7
  bcc1_45Fall_10010002    1965    32.2
  bcc1_45Fall_10010002    1966    85.4

「HUC8」列のすべてのデータを単に に置き換えることができればfilename完璧です。追加の列である必要はありません。

これを何千ものファイルに対して実行する必要があります。

最初の部分のやり方がわかれば、ループを作成できます。しかし、もっと良い方法があるかもしれません。

どこから始めればいいのか分かりません。

答え1

awkと の使用column:

$ awk '
  NR==1{ sub(/\.csv$/, "", FILENAME) } # remove .csv suffix from FILENAME
  NR>1{ $1=FILENAME }                  # replace the first field with filename
  1                                    # print record
' bcc1_45Fall_10010002.csv | column -t
HUC8                  YEAR  RO_MM
bcc1_45Fall_10010002  1961  74.7
bcc1_45Fall_10010002  1962  69.1
bcc1_45Fall_10010002  1963  52.0
bcc1_45Fall_10010002  1964  130.7
bcc1_45Fall_10010002  1965  32.2
bcc1_45Fall_10010002  1966  85.4

これをシェル ループで実行すると、変更されたファイルをディレクトリに保存できますmodified_files

mkdir modified_files &&
for i in *.csv; do
  awk 'NR==1{ sub(/\.csv$/, "", FILENAME) } NR>1{ $1=FILENAME }1' "$i" |
    column -t > "./modified_files/$i"
done

列を置き換える必要がありHUC8、これが最初の列ではない場合は、コードを次のように変更します。

awk -v search='HUC8' '
  NR==1{
    for(i=1;i<=NF;i++)
      if ($i==search){ fld=i; sub(/\.csv$/, "", FILENAME); break }
  }
  NR>1{ $fld=FILENAME }
  1
' file.csv | column -t

答え2

使用ミラーファイルが「単純な」CSV(カンマなし)であると仮定します内でフィールドなど(完全なRFC-4180サポートが必要な場合は--csvlite変更できます)--csv

$ cat bcc1_45Fall_10010002.csv
HUC8,YEAR,RO_MM
10010002,1961,74.7
10010002,1962,69.1
10010002,1963,52.0
10010002,1964,130.7
10010002,1965,32.2
10010002,1966,85.4

それから

  1. 現在のHUC8列を置き換えるには:

     $ mlr --csvlite put -S '$HUC8 = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv
     HUC8,YEAR,RO_MM
     bcc1_45Fall_10010002,1961,74.7
     bcc1_45Fall_10010002,1962,69.1
     bcc1_45Fall_10010002,1963,52.0
     bcc1_45Fall_10010002,1964,130.7
     bcc1_45Fall_10010002,1965,32.2
     bcc1_45Fall_10010002,1966,85.4
    
  2. 別のName列を追加するには:

     $ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' bcc1_45Fall_10010002.csv
     HUC8,YEAR,RO_MM,Name
     10010002,1961,74.7,bcc1_45Fall_10010002
     10010002,1962,69.1,bcc1_45Fall_10010002
     10010002,1963,52.0,bcc1_45Fall_10010002
     10010002,1964,130.7,bcc1_45Fall_10010002
     10010002,1965,32.2,bcc1_45Fall_10010002
     10010002,1966,85.4,bcc1_45Fall_10010002
    
  3. Name最初の列として列を追加するには:

     $ mlr --csvlite put -S '$Name = substr(FILENAME,0,-5)' then reorder -f Name bcc1_45Fall_10010002.csv
     Name,HUC8,YEAR,RO_MM
     bcc1_45Fall_10010002,10010002,1961,74.7
     bcc1_45Fall_10010002,10010002,1962,69.1
     bcc1_45Fall_10010002,10010002,1963,52.0
     bcc1_45Fall_10010002,10010002,1964,130.7
     bcc1_45Fall_10010002,10010002,1965,32.2
     bcc1_45Fall_10010002,10010002,1966,85.4
    

上記のすべては結果を標準出力に書き込みます。ファイルをその場で変更するには、オプションを追加します。シェル グロブ (例:または-I) を使用して、複数のファイルを一度に渡すことができます。bcc*.csv*.csv

[テスト時それなし -Iレコードの異質性の結果として新しいヘッダーが必要にならない限り、ヘッダー行は繰り返されません。ただし、-I適切なヘッダーが各ファイルに追加されます。

答え3

$ perl -lne 'BEGIN {$fnr=1};

             if ($fnr == 1) {
               ($fn = $ARGV) =~ s/\.[^.]+$//;
               print "NAME,$_"
             } else {
               print "$fn,$_"
             };

             $fnr++;

             if (eof) {$fnr=1}' *.csv

これにより、ファイル名 (.csv 拡張子なし) が最初のフィールドとして追加され、.csv ファイルの内容が stdout に出力されます。

とは異なりawkperlは各ファイルの行数を追跡しません (変数を使用して合計行数のみを追跡します$.)。このスクリプトは、最初に$fnrBEGIN ブロックで変数を設定し、次に読み取られる行ごとに変数を増分し、最後にファイルの末尾に到達するたびに変数を 1 にリセットすることで、手動で行数を維持します。

これを変更して、ファイル名を最初のフィールドではなく最後のフィールドとして追加することは簡単です。たとえば、2 つのprintステートメントを次のように変更します。

      print "$_,NAME"
and: 
      print "$_,$fn"

ファイル名フィールドを最初のフィールドではなく、行内の別の場所に挿入する必要がある場合は、perl のsplice関数を使用できます。

たとえば、次のコードはファイル名を 3 番目のフィールドとして挿入します (perl 配列のインデックスは 1 ではなく 0 から始まるため、3 番目のフィールドは$F[2]ではなく になることに注意してください$F[3])。

$ perl -F, -lne 'BEGIN {$fnr=1; $field_num=2};

             if ($fnr == 1) {
               ($fn = $ARGV) =~ s/\.[^.]+$//;
               splice @F, $field_num, 0, "NAME";
             } else {
               splice @F, $field_num, 0, $fn;
             };

             print join(",", @F);

             $fnr++;

             if (eof) {$fnr=1}' *.csv

これは、-Fフィールド区切り文字としてコンマを設定する perl のオプションを使用します。これにより、perl の自動分割機能も有効になり、入力行が と呼ばれる配列に自動的に分割されます@F(これは、入力行を $1、$2、$3 などに自動的に分割する awk のデフォルトの動作に似ています)。リテラル文字列 "NAME" または変更されたファイル名のいずれかが @F にスプライスされ、配列の要素が@Fコンマ文字で結合されて出力されます。

最後に、ファイルの内容を実際に変更したい場合は、perl の-iオプションを使用します。オプションで「拡張子」を使用することで、元のファイルのバックアップを保持することもできます。たとえば、でに-i名前を変更します。例:filename.csvfilename.csv.orig-iorig

perl -iorig -lne '......' *.csv

または

perl -iorig -F, -lne '......' *.csv

答え4

次にファイル名をループし、awkで列を出力します。

for f in *.csv;
do
    head -1 $f > out/$f
    cat $f | awk -v FIN=${f%.csv} 'NR>1 {print FIN, $2, $3}' >> out/$f
done

HUC8       YEAR    RO_MM
bcc1_45Fall_10010002 1961 74.7 
(...)

関連情報