두 단어 사이의 특정 문자를 CR+LF로 바꾸는 방법

두 단어 사이의 특정 문자를 CR+LF로 바꾸는 방법

어떤 서버에 특정 파일과 소유권이 있는지 알려주는 csv 파일을 생성하고 싶습니다. 이것은 내가 얻는 원시 출력입니다.

server01,server02,server03,owner,/etc/file1
server04,owner,/etc/file2
server05,server06,owner,/etc/file3

서버 이름 사이의 쉼표를 Windows 형식 줄 바꿈(CF+LR)으로 바꾸고 그 사이에 따옴표를 추가하여 CSV가 동일한 상자에 모든 서버를 표시하도록 하고 싶습니다.

원하는 출력:

"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3

이를 달성하기 위해 sed를 사용하는 방법은 무엇입니까?

답변1

,owner,필드에 래핑하려는 나머지 텍스트가 모두 있는 경우:

GNU 사용 sed:

sed -E ':1; s/,(.*,owner,)/\r\n\1/; t1; s/(.*)(,owner,)/"\1"\2/' file

그리고 perl:

perl -pe 's{.*?(?=,owner,)}{q(") . ($& =~ s/,/\r\n/gr) . q(")}e' file

마지막 두 필드를 제외한 모든 항목이 있는 경우:

GNU 사용 sed:

sed -E ':1; s/(.*),(.*,.*,)/\1\r\n\2/; t1; s/(.*),/"\1",/' file

그리고 perl:

perl -pe 's{(.*)(?=,.*,)}{q(") . ($& =~ s/,/\r\n/gr) . q(")}e' file

또는 Text::CSV올바른 CSV 구문 분석 및 형식화를 위해 Perl 모듈을 사용하십시오.

perl -MText::CSV -e '
  $csv = Text::CSV->new({binary => 1, decode_utf8 => 0, eol => $" = "\r\n"});
  while ($row = $csv->getline(STDIN)) {
    if (($last = $#{$row}) > 1) {
      $csv->print(STDOUT, ["@$row[0..$last-2]", @$row[$last-1..$last]]);
    } else {
      $csv->print(STDOUT, $row);
    }
  }' < file

파일이 BOM과 함께 UTF-16 또는 UTF-8로 인코딩된 경우(Microsoft 파일에서는 전례가 없는 일임) 조정해야 하거나(이 방법은 참고자료 참조) 어떤 방식으로 인코딩해야 할 수도 있습니다. 정상적인 입력을 처리할 수 있도록 perldoc Text::CSV형식을 다시 지정하는 방법입니다 .<file dos2unix | ... | unix2dos

답변2

나는 이것을 하지 않을 것입니다 sed. 나는 다음을 사용할 것입니다 perl(또는 아마도 awk- 그러나 Perl 내장 기능을 사용하는 대신 내 자신의 pop()함수를 작성해야 합니다):join()

$ perl -F, -lane '$file = pop @F; $owner = pop @F;
                  print join(",", "\"" . join("\r\n", @F) . "\"", $owner, $file)' input.csv 
"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3

먼저, 배열에서 마지막 두 요소(소유자와 파일 이름)를 제거하고 @F( 옵션이 쉼표를 필드 구분 기호로 사용하도록 지정하기 @F때문에 각 입력 행에 대해 자동으로 생성됨 - awk가 입력을 자동으로 분할하는 방법과 유사함) 추가합니다. 변수 sum 에 저장됩니다. .-a-F,$file$owner

"\"" . join("\r\n", @F) . "\""@F의 각 요소가 CR+LF 문자로 구분되고 전체 문자열이 큰따옴표로 묶인 문자열을 구성합니다.

$owner문자열은 및 및 와 연결되어(쉼표로) $file인쇄됩니다.

답변3

각 레코드의 마지막 두 필드까지(포함하지 않음) 다음의 모든 필드와 결합된 첫 번째 쉼표로 구분된 필드를 인용하여 인용된 CSV 필드를 생성하려고 합니다. 그런 다음 결합된 필드에 포함된 쉼표를 CR+LF로 바꿔야 합니다.

파일의 각 줄 내용을 바꾸고, 두 번째 쉼표 뒤에 큰따옴표를 삽입하고, 줄을 다시 뒤집고, 시작 부분에 큰따옴표를 삽입하면 쉽게 이 작업을 수행할 수 있습니다.

$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/'
"server01,server02,server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05,server06",owner,/etc/file3

이제 올바르게 참조된 헤더 없는 CSV 파일이 있으므로 다음을 사용할 수 있습니다.밀러( mlr; 도구구체적으로구조화된 데이터(예: CSV)를 처리하려면 첫 번째 필드의 모든 쉼표를 CR+LF로 바꾸세요.

$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N put '$1 = gsub($1, ",", "\r\f")'
server01
server02
server03,owner,/etc/file1
server04,owner,/etc/file2
server05
server06,owner,/etc/file3

Unix 시스템에서는 필드 및 레코드 구분 기호가 이 데이터 세트의 필드에 포함되어 있지 않으므로 필드를 인용할 필요가 없습니다. 별도의 호출을 통해 각 레코드에서 두 번째 필드를 추출하여 이를 표시할 수 있습니다 mlr.

$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N put '$1 = gsub($1, ",", "\r\f")' | mlr --csv -N cut -f 2
owner
owner
owner

sed마지막 명령 출력 의 원래 인용문을 유지하려면 다음을 사용하십시오 --quote-original.

$ rev file | sed 's/,/,"/2' | rev | sed 's/^/"/' | mlr --csv -N --quote-original put '$1 = gsub($1, ",", "\r\f")'
"server01
server02
server03",owner,/etc/file1
"server04",owner,/etc/file2
"server05
server06",owner,/etc/file3

그러나 이상적으로는 사후 처리 단계에서 필드를 수정하는 대신 처음부터 필드가 올바르도록 손상된 CSV 출력을 생성하는 코드를 수정해야 합니다.


Miller만 사용:

mlr --nidx --fs comma put '
    for (var i=2; NF > 3; i=i+1) {
        $1 .= "\r\f" . $[i];
        unset $[i]
    }
    $1 = "\"" . $1 . "\""' file

이는 파일을 간단한 쉼표로 구분되고 암시적으로 색인화된 텍스트 파일로 읽습니다. 그런 다음 CR+LF를 구분 기호로 사용하여 두 번째 및 후속 필드를 첫 번째 필드 끝에 추가하고 세 개의 필드만 남을 때까지 각 추가 필드를 제거합니다. 그런 다음 첫 번째 필드를 명시적으로 참조합니다.

관련 정보