여러 csv 파일에서 특정 문자열 찾기 및 바꾸기

여러 csv 파일에서 특정 문자열 찾기 및 바꾸기

다음 형식의 CSV 파일이 여러 개 있습니다.

"TIMESTAMP",col2,col3,col4
"yyyy-mm-dd HH:mm",20,19,17

나는 결국 다음과 같이 -교체 하고 싶습니다 ./

TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

다음 명령을 사용하여 파일을 첨부했지만 아무 일도 일어나지 않습니다.

find -name '*.csv' -exec awk '{gsub(/-/, "/",$1)}' '{}' \;

내가 무엇을 놓치고 있나요? 도와주세요

답변1

find . -name '*.csv' -type f -size +10c -exec perl -pi -e '
  s{^(\d\d\d\d)-(\d\d)-(\d\d)\b}{$1/$2/$3}' {} +

-줄 시작 부분의 타임스탬프에 있는 s 만 대체되고 다른 모든 -이벤트는 무시됩니다.

.csv해당 타임스탬프가 포함되지 않은 파일도 포함하여 모든 파일을 대체합니다 . 이를 방지하려면 GNU를 사용하여 grep다음을 수행할 수 있습니다.

grep -rlPZ --include='*.csv' '^\d\d\d\d-\d\d-\d\d\b' . |
  xargs -r0 perl -pi -e '
  s{^(\d\d\d\d)-(\d\d)-(\d\d)\b}{$1/$2/$3}' {} +

답변2

명령 은 awk실제로 원하는 변경을 수행하지만(쉼표로 구분된 필드 대신 첫 번째 공백으로 구분된 필드를 변경하는 경우 제외) 인쇄하라고 지시하지 않았기 때문에 인쇄되지 않습니다.

$ cat  file.csv
TIMESTAMP,col2,col3,col-4
yyyy-mm-dd HH:mm,20,19,17
$ awk '{gsub(/-/, "/",$1)}' file.csv 
$ 

위에서 볼 수 있듯이 명령에는 출력이 없습니다. 비교:

$ awk -F, -v OFS=, '{gsub(/-/, "/", $1); print}' file.csv 
TIMESTAMP,col2,col3,col-4
yyyy/mm/dd HH:mm,20,19,17

그러나 이는 단순히 모든 파일의 내용을 표준 출력으로 인쇄하기 때문에 원하는 결과가 아닐 수 있습니다. 실제 파일을 수정하려면 다음을 시도하십시오.

$ perl -i -F, -lane '$F[0] =~ s|-|/|g; print join ",",@F' file.csv 
$ cat file.csv
TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

만일을 대비해 -i.bak확장자를 가진 원본 파일의 복사본을 만들어 사용하는 것이 좋습니다 . .bak그런 다음 이를 다음과 같이 명령에 통합할 수 있습니다.

find . -name '*.csv' -type f -exec perl -i.bak -F, -lane '
  $F[0] =~ y|-|/|; print join ",", @F' '{}' +

또는 GNU awk( )가 있고 gawk현재 작업 디렉터리에 infile또는 이름의 파일이 포함되어 있지 않다는 것을 보장할 수 있는 infile.awk경우 다음을 수행할 수 있습니다.

find . -name '*.csv' -type f -exec gawk -F, -v OFS=, -i inplace '
  {gsub(/-/, "/",$1); print}' '{}' +

답변3

질문에 표시된 예시 입력과 예상 출력을 귀하가 말한 내용과 병합하세요.코멘트타임스탬프 값에 실제로 어떤 일이 발생합니까?

$ cat foo.csv
"TIMESTAMP",col2,col3,col4
""yyyy-mm-dd HH:mm"",20,19,17

그런 다음 GNU awk를 사용하여 다음을 수행하십시오 -i inplace.

$ find . -name 'foo.csv' -exec awk -i inplace '{gsub(/"/,""); gsub(/-/,"/"); print}' {} +

$ cat foo.csv
TIMESTAMP,col2,col3,col4
yyyy/mm/dd HH:mm,20,19,17

또는 GNU awk 스크립트(for -i)를 다음 GNU sed 스크립트로 바꿀 수 있습니다.

sed -i 's:"::g; s:-:/:g'

"s 또는 s를 다른 곳에서 바꾸고 싶지 않고 -참조 필드 내에 "s, s 또는 개행 문자가 없는 경우 ,awk 스크립트를 다음과 같이 변경하세요.

BEGIN{FS=OFS=","} {gsub(/"/,"",$1); gsub(/-/,"/",$1); print}

답변4

사용행복하다(이전 Perl_6)

간단한 방법:

~$ raku -pe 's:g{  \w**4  <( (\-)  (\w**2)  (\-) )>  \w**2 } = "/$1/";'  file

CSV 콘텐츠 검증:

~$ raku -MText::CSV -e 'my @a = csv(in => $*IN, sep => ",");   \
                        @a>>.[0] = @a>>.[0].map:               \
                        *.subst(:global, / \w**4 <( (\-) (\w**2) (\-) )> \w**2 /, {"/$1/"} );  \
                        csv(in => @a, out => $*OUT, sep => ",");'  < file

다음은 Perl 계열의 프로그래밍 언어인 Raku로 작성한 답변입니다. 첫 번째 답변은 익숙한 s///대체 관용구를 사용하지만 여기서 Raku는 새로운 형식 옵션을 추가했습니다: s{original} = "replacement". 문자/숫자 및 대시(하이픈)의 올바른 조합과 일치하는 항목을 찾는 Raku의 <(…)>캡처 플래그는 원하는 블록 외부의 모든 항목을 제거하는 데 사용되며 이는 교체 시 변경됩니다.

두 번째 답변은 Raku의 Text::CSV모듈을 사용하여 CSV 입력/출력을 검증합니다. 샘플 데이터만 사용첫번째 줄대체됩니다(이 @a>>.[0]관용어는 데이터의 첫 번째 열에만 해당됩니다).

입력 예:

"TIMESTAMP",col2,col3,col4
"yyyy-mm-dd HH:mm",20,19,17

출력 예(첫 번째 코드 예):

"TIMESTAMP",col2,col3,col4
"yyyy/mm/dd HH:mm",20,19,17

출력 예(두 번째 코드 예, CSV 출력 확인):

TIMESTAMP,col2,col3,col4
"yyyy/mm/dd HH:mm",20,19,17

Text::CSV이를 변경해야 하는 경우 Raku 모듈에는 출력 열을 참조하기 위한 다양한 옵션이 있습니다(기본값은 공백이 포함된 열 단위 요소를 참조하는 것입니다). 쉘 글로빙에 의존하지 않고 코드 본문에서 입력을 얻을 수도 있습니다. csv(in => $*IN, sep => ",");코드 부분을 다음과 같이 변경 하십시오 .

csv(in => "path/to/file", sep => ",");


https://raku.land/zef:Tux/Text::CSV
https://docs.raku.org/routine/dir
https://raku.org

관련 정보