
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
や結合文字を含む名前は適切に処理されません。
(入力がサンプル内の引用符なしの単純な csv だけではない場合は、perl に適切な 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 SerRA
perl
\u
\u\L
awk
tolower()
コメントで指摘されているように、組み込みコマンドのみを使用しなければならない場合、bash
bashは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
'sは alnums + アンダースコアなので、 のような\w
文字列でも違いが見られます。 はを大文字にしますが、他の文字列では を大文字にします。特定の入力に合わせて調整する必要があるかもしれません(文字を組み合わせることも検討してください。これもここで問題を引き起こします)。jean_pierre
perl
Jean_pierre
Jean_Pierre
Lingua::EN::NameCase
perl
さらに特殊なケースを処理するためのモジュール。
どのコマンドがどのシステムにデフォルトでインストールされるかという点については、ほとんどのシステムにはperl
(Text::CSV
モジュールは存在するかもしれませんが、おそらく 1 つではないでしょうLingua::EN::NameCase
) と POSIX 準拠の実装awk
がありsh
、多くのシステム (一部の非 GNU システムも) にはbash
(GNU シェル) があり、いくつかのシステムには GNU awk があります (ただし、Ubuntu などの一部の GNU ベースのシステムには存在しません。Ubuntu は少なくとも一部のバージョンでは mawk を優先します)。現在、zsh
デフォルトでインストールされているシステムはほとんどありません。
CentOS は GNU システムなので、 に加えてbash
、 がgawk
デフォルトでインストールされているはずですperl
。bash
さらに、とgawk
も提供されています。sh
awk
答え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
からの使用csvキットCSVファイルをJSONに変換し、jq
変更したデータをCSVとして出力する前に:
csvjson -H file |
jq -r '
.[].e |= gsub(
"(?<a>[[:alnum:]]+)";
.a | sub("(?<b>.)"; .b | ascii_upcase)) |
.[] | map(.) | @csv'
このcsvjson
コマンドは、CSV ファイルを JSON ドキュメントに変換します。このドキュメントでは、元の CSV 行ごとに 1 つのオブジェクトを持つ配列の各列にアルファベットのキーが付けられます。式は各オブジェクトからjq
5 番目の列 ( ) を選択し、そこに含まれる各単語を抽出します。各単語の最初の文字は の関数e
を使用して大文字に変換され、結果は適切に引用符で囲まれた CSV データとして出力されます。ascii_upcase
jq
質問のデータを考慮すると、次のようになります
1,,,,"Ivan Petrov",,67,
2,2,,,"Vasia Pupkin","director",8,
3,,,,"John Lenon",,,
これにより、埋め込まれたコンマや改行を含む CSV フィールドにも対応できます。