경고: 완전 초보자입니다. .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
사용 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
그 다음에
현재
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
별도의
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
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
(...)