Bash: csv 필드의 제목 케이스

Bash: csv 필드의 제목 케이스

CentOS 시스템에 다음 입력 파일이 있습니다.

1,,,,ivan petrov,,67,
2,2,,,Vasia pupkin,director,8,
3,,,,john Lenon,,,

작업은 다음과 같이 변경하는 것입니다.

1,,,,Ivan Petrov,,67,
2,2,,,Vasia Pupkin,director,8,
3,,,,John Lenon,,,

이름과 성은 대문자로 시작해야 합니다.

#!/bin/bash
while IFS="," read line
do
    ns=$(echo $line | awk -F, '{print $5}')
    name=$(echo $ns | awk '{print $1}')
    surname=$(echo $ns | awk '{print $2}')
    ns=$(echo ${name^} ${surname^})
    awk -v nm="$ns" 'BEGIN{FS=OFS=","}{$5=nm}1' accnew.csv
done < <(tail -n +2 accnew.csv) > 1new.csv

그게 내 스크립트인데 제대로 작동하지 않습니다.

답변1

텍스트를 처리하기 위해 쉘 루프를 사용하지 마십시오. 텍스트 처리 유틸리티를 사용하십시오.

여기에서 5 번째 필드 의 이름을 대문자로 사용하려면Lingua::EN::NameCase perl모듈을 사용할 수 있습니다:

perl -Mopen=locale -MLingua::EN::NameCase -F, -ae '
  $F[4] = nc $F[4] unless @F < 5;
  print join ",", @F' < your-file

그렇지 않은 경우 대략적으로 하나 이상의 영숫자 시퀀스의 모든 첫 번째 문자를 대문자로 변환할 수 있습니다.

perl -Mopen=locale -F, -ae '
  $F[4] =~ s/\w+/\u$&/g unless @F < 5;
  print join ",", @F' < your-file

그러나 McGregor, van Dike...와 같은 이름이나 결합 문자가 있는 이름은 제대로 처리되지 않습니다.

(Perl에는 입력이 샘플에 인용되지 않은 단순한 csv가 아닌 경우를 대비해 적절한 CSV 구문 분석 모듈도 있습니다).

표준 구문으로도 동일한 작업을 수행할 수 있지만 awk훨씬 더 번거롭습니다.

awk -F, -v OFS=, '
  NF >= 5 {
    r = $5; $5 = ""
    while (match(r, "[[:alnum:]]+")) {
      $5 = $5 substr(r, 1, RSTART - 1) \
           toupper(substr(r, RSTART, 1)) \
           substr(r, RSTART + 1, RLENGTH - 1)
      r = substr(r, RSTART + RLENGTH)
    }
    $5 = $5 r
  }
  {print}' < your-file

GNU awk와 그 patsplit()기능을 사용하면 약간 더 쉽습니다.

gawk -F, -v OFS=, '
  NF >= 5 {
    n = patsplit($5, f, /[[:alnum:]]+/, s)
    $5 = s[0]
    for (i = 1; i <= n; i++)
      $5 = $5 toupper(substr(f[i], 1, 1)) \
              substr(f[i], 2) s[i]
  }
  {print}' < your-file

쉘 루프를 사용해야 한다면 최소한 대문자 연산자와 함께 쉘을 사용하십시오:

#! /bin/zsh -
while IFS=, read -ru3 -A fields; do
  (( $#fields < 5 )) || fields[5]=${(C)fields[5]}
  print -r -- ${(j[,])fields} || exit
done 3< your-file

하나(및 기반 하나)는 예를 들어 대신으로 변한다 Lingua::EN::NameCase는 점에서 다른 것과 다릅니다 . in 과 in을 각 단어의 두 번째 부분에 적용하면 동일한 결과를 얻을 수 있습니다 .éric serRAÉric SerraÉric SerRAperl\u\u\Lawktolower()

주석에 표시된 대로 및 해당 내장 명령 만 사용해야 한다면 bashbash는 예를 들어 zsh 또는 ksh93에 비해 연산자가 매우 제한되어 있으므로 훨씬 더 번거로울 것입니다(비효율적일 뿐만 아니라).read -a분리된 값을 읽을 수 없습니다.

이는 다음과 같아야 합니다(여기서는 연산자가 bash 4.0 이상이라고 가정 ${var^}).

#! /bin/bash -
set -o noglob -o nounset
IFS=,
re='^([^[:alnum:]]*)([[:alnum:]]+)(.*)$'
while IFS= read -ru3 line; do
  fields=( $line'' )
  if (( ${#fields[@]} >= 5 )); then
    rest="${fields[4]}" fields[4]=
    while [[ "$rest" =~ $re ]]; do
      fields[4]="${fields[4]}${BASH_REMATCH[1]}${BASH_REMATCH[2]^}"
      rest="${BASH_REMATCH[3]}"
    done
  fi
  printf '%s\n' "${fields[*]}" || exit
done 3< your-file

이들은 입력이 사용자의 로케일 문자 세트로 인코딩된 유효한 텍스트라고 가정합니다(예를 들어, UTF-8 로케일에서 é위의 내용은 iso8859-1 또는 기타 문자 세트가 아닌 UTF-8(0xc3 0xa9 바이트)로 인코딩됩니다). bash(및 아마도 awk)는 NUL 바이트를 질식시킵니다.

perl은 alnums + 밑줄 이므로 다른 문자열은 대문자로 대문자로 표기하는 것과 같은 문자열 \w에 대한 차이점도 찾을 수 있습니다 . 특정 입력에 적응해야 할 수도 있습니다(여기서 작업에 스패너를 추가하는 문자 결합도 고려하십시오). 또한 참조하십시오jean_pierreperlJean_pierreJean_PierreLingua::EN::NameCase perl훨씬 더 특별한 경우를 처리하는 모듈입니다.

어떤 시스템에 기본적으로 어떤 명령이 설치되어 있는지. 대부분의 시스템에는 모듈이 있을 perl수 있지만 하나 Text::CSV는 아닐 가능성이 높으며 Lingua::EN::NameCasePOSIX 호환 awksh구현이 있으며 많은 시스템(일부 GNU가 아닌 시스템도 포함) bash이 GNU 쉘을 갖고 있으며 일부 시스템에는 GNU awk가 있습니다(일부 GNU 기반 시스템은 아님). 적어도 일부 버전에서는 mawk를 선호하는 Ubuntu와 같은). 현재 zsh기본적으로 설치된 것은 거의 없습니다.

GNU 시스템인 CentOS는 bash기본적 gawk으로 perl. bash심지어 거기 에 gawk제공합니다 .shawk

답변2

모든 입력이 게시된 예와 같이 중간 단어 대문자가 없는 모든 영어 문자의 간단한 2단어 이름인 경우 모든 Unix 상자의 모든 쉘에서 awk를 사용하십시오.

$ awk '
    BEGIN { FS=OFS="," }
    { split($5,ns," "); $5 = uc(ns[1]) " " uc(ns[2]) }
    { print }
    function uc(str) { return toupper(substr(str,1,1)) substr(str,2) }
' file
1,,,,Ivan Petrov,,67,
2,2,,,Vasia Pupkin,director,8,
3,,,,John Lenon,,,

답변3

bash의 대안은 다음과 같습니다.

while IFS=, read -ra fields; do
  read -ra name <<<"${fields[4]}"
  fields[4]=${name[*]^}
  (IFS=,; echo "${fields[*]}")
done < file
1,,,,Ivan Petrov,,67
2,2,,,Vasia Pupkin,director,8
3,,,,John Lenon,,

그리고 펄

perl -F, -lane '
    $F[4] = join " ", map {ucfirst} split " ", $F[4];
    print join ",", @F;
' file

답변4

csvjson에서 사용csvkitCSV 파일을 JSON으로 변환한 다음 다음을 사용하여 수정합니다.jq수정된 데이터를 CSV로 출력하기 전에:

csvjson -H file |
jq -r '
    .[].e |= gsub(
        "(?<a>[[:alnum:]]+)"; 
        .a | sub("(?<b>.)"; .b | ascii_upcase)) |
    .[] | map(.) | @csv'

csvjson명령은 원래 CSV 줄당 하나의 개체가 있는 배열의 각 열에 대한 알파벳 키를 사용하여 CSV 파일을 JSON 문서로 변환합니다. 표현식 은 각 객체에서 jq5번째( ) 열을 선택 하고 그 안의 각 단어를 추출합니다. 각 단어의 첫 번째 문자는 의 함수를 e사용하여 대문자로 변환되고 , 그 결과는 적절하게 인용된 CSV 데이터로 출력됩니다.ascii_upcasejq

질문의 데이터를 고려하면 다음과 같은 결과가 나옵니다.

1,,,,"Ivan Petrov",,67,
2,2,,,"Vasia Pupkin","director",8,
3,,,,"John Lenon",,,

이는 쉼표와 개행 문자가 포함된 CSV 필드에도 대처할 수 있습니다.

관련 정보