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가 된다는 점에서 다른 것과 다릅니다 . 각 단어의 두 번째 부분에 적용 하여 동일한 결과를 얻을 수 있습니다 .éric serRAÉric SerraÉric SerRAperl\u\u\Lawktolower()

bashbash는 zsh 또는 ksh93에 비해 연산자가 매우 제한되어 있기 때문에 내장 명령 만 사용해야 한다면 (비효율적일 뿐만 아니라) 더 문제가 될 것입니다.read -a구분된 값을 읽을 수 없습니다..

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

#! /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은 숫자 + 밑줄 이므로 as 가 대문자로 표시된 문자열 과 as가 대문자로 표시된 \w문자열 간의 차이도 알 수 있습니다 . 이를 특정 입력에 맞게 조정해야 할 수도 있습니다(여기서 작업에 렌치가 추가되는 문자 결합도 고려하십시오). 또한보십시오jean_pierreperlJean_pierreJean_PierreLingua::EN::NameCase perl더 특별한 경우를 처리하는 모듈입니다.

기본적으로 명령이 설치되는 시스템은 무엇입니까? 대부분의 시스템에는 perl( Text::CSV모듈이 있을 수도 있지만 그렇지 않을 수도 있음 Lingua::EN::NameCase) POSIX 호환성 awksh구현이 있고, 많은 시스템(일부 GNU가 아닌 시스템도)에는 bashGNU 쉘이 있고 일부 시스템에는 Ubuntu와 같은 GNU awk(일부 GNU 기반 시스템은 아니지만)가 있습니다. 적어도 일부 버전에서는 mawk를 선호합니다). 현재 zsh기본적으로 설치되는 것은 거의 없습니다 .

CentOS를 GNU 시스템으로 bash이외에도 제공 하고 있는 gawk경우 perl도 있습니다 .bashgawkshawk

답변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

또 다른 배쉬 방법:

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 파일을 JSON 문서로 변환합니다. 여기서 배열의 각 열은 알파벳 키로 정렬되며 각 원본 CSV 행에 대해 하나의 개체가 포함됩니다. 이 표현식은 각 객체에서 다섯 번째( ) 열을 선택 jq하고 그 안에 있는 각 단어를 추출합니다. 사용된 함수는 e각 단어의 첫 문자를 대문자로 변환한 후 그 결과를 올바르게 인용된 CSV 데이터로 출력합니다.ascii_upcasejq

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

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

이는 삽입된 쉼표와 줄 바꿈이 포함된 CSV 필드도 처리합니다.

관련 정보