열 추가, 전체 열을 파일 이름(".csv" 제외)으로 채우기 - BaSH의 여러 파일에 대해

열 추가, 전체 열을 파일 이름(".csv" 제외)으로 채우기 - BaSH의 여러 파일에 대해

경고: 완전 초보자입니다. .csv 파일에 열을 추가해야 합니다. 여기서 열 헤더는 "이름"일 수 있지만 전체 열은 파일 자체의 이름, .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

사용 awkcolumn:

$ 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옵션을 추가하세요. Shell glob ex를 사용하여 한 번에 여러 파일을 전달할 수 있습니다. 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 파일의 내용이 표준 출력에 인쇄됩니다.

awk와 달리 perl각 개별 파일의 줄 수를 추적하지 않습니다(변수가 있는 총 줄 수만 추적함 $.). 스크립트는 먼저 BEGIN 블록에 변수를 설정한 $fnr다음 읽은 각 줄에 대해 변수를 증가시키고 마지막으로 파일 끝에 도달할 때마다 다시 1로 재설정하여 이 수를 수동으로 유지 관리합니다.

파일 이름을 첫 번째 필드가 아닌 마지막 필드로 추가하도록 쉽게 수정됩니다. 예를 들어 print두 명령문을 다음과 같이 변경합니다.

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

첫 번째 필드가 아닌 줄의 다른 곳에 파일 이름 필드를 삽입해야 하는 경우 Perl의 splice기능을 사용할 수 있습니다.

예를 들어, 다음은 파일 이름을 세 번째 필드로 삽입합니다(perl 배열 인덱싱은 1이 아닌 0에서 시작하므로 세 번째 필드는 $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

이는 Perl의 -F옵션을 사용하여 쉼표를 필드 구분 기호로 설정합니다. 이는 또한 Perl의 자동 분할 기능을 통해 입력 행을 이름이 지정된 배열로 자동 분할할 수 있습니다 @F(이는 입력 행을 $1, $2, $3 등으로 자동 분할하는 awk의 기본 동작과 유사합니다). 리터럴 문자열 "NAME" 또는 수정된 파일 이름을 @F로 연결한 다음 @F쉼표 문자로 연결된 배열 요소를 인쇄합니다.

마지막으로 파일의 내용을 실제로 변경하려면 Perl의 -i옵션을 사용하십시오. -i이름 바꾸기와 같은 옵션과 함께 "확장자"를 사용하여 원본 파일의 백업을 유지하도록 선택할 수 있습니다 filename.csv.filename.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 
(...)

관련 정보