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

そうでない場合は、1つ以上の英数字で構成される各シーケンスの最初の文字を大文字に変換できます。

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

GNUawkとその機能を使うの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

これらの1つ(およびそれに基づいているもの)は、インスタンスではなくインスタンスになるというLingua::EN::NameCase点で他のものとは異なります。各単語の2番目の部分に適用して同じ結果を得ることができます。é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より特別なケースを処理するモジュールです。

デフォルトでは、コマンドがインストールされるシステムは何ですか?ほとんどのシステムにはperlText::CSVモジュールがあるかもしれませんが、そうではないかもしれません)、 Lingua::EN::NameCasePOSIXawksh互換性と実装があり、多くのシステム(一部のGNUではないシステムも)はGNUbashシェルを持ち、一部のシステムにはUbuntuなどのGNU awk(一部のGNUベースのシステムではありませんが)があります。少なくともいくつかのバージョンでは、mawkを好む)。現在zshデフォルトでインストールされることはほとんどありません。

CentOSをGNUシステムとしてbash以外にも提供しているgawk場合perlもあります。bashgawkshawk

おすすめ記事