
我在 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
不要使用 shell 循環來處理文本。使用文字處理實用程式。
這裡,要將第 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
使用 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
如果必須使用 shell 循環,至少使用帶有大寫運算子的 shell:
#! /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 SerRA
實例。perl
透過將\u
to\u\L
和 inawk
應用於tolower()
每個單字的第二部分,您可以獲得相同的結果。
如果您必須僅使用bash
及其內建命令(如您在註釋中指出的那樣),那將更加麻煩(除了效率低之外),因為與zsh 或ksh93 相比,bash 的操作符非常有限,而且它的操作符也非常有限。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 區域設定中,上面的內容é
以UTF-8(0xc3 0xa9 位元組)編碼,而不是iso8859-1 或其他字元集) 。 bash(可能還有 awk)會因 NUL 位元組而阻塞。
由於perl
'\w
是 alnums + 下劃線,因此您還會發現字串之間的差異,其中jean_pierre
的perl
字串大寫為 as,Jean_pierre
而其他字串則大寫為Jean_Pierre
。您可能需要適應您的特定輸入(也可以考慮組合字符,這也會在此處的工作中添加扳手)。另請參閱Lingua::EN::NameCase
perl
模組來處理更多特殊情況。
至於預設安裝在什麼系統上的命令。大多數系統都會有perl
(可能是Text::CSV
模組,但可能不是那個Lingua::EN::NameCase
)和 POSIX 相容awk
和sh
實現,許多(甚至一些非 GNU 系統)有bash
(GNU shell),有些有 GNU awk(儘管不是一些基於 GNU 的系統)例如Ubuntu ,至少在某些版本中喜歡mawk)。目前很少有zsh
預設安裝的。
CentOS 作為 GNU 系統bash
,gawk
除了perl
.bash
甚至gawk
提供sh
和awk
那裡。
答案2
如果您的所有輸入都是所有英文字母的簡單 2 個單字名稱,沒有中間單字大寫,就像您發布的範例中那樣,那麼在每個 Unix 機器上的任何 shell 中使用任何 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
perl -F, -lane '
$F[4] = join " ", map {ucfirst} split " ", $F[4];
print join ",", @F;
' file
答案4
使用csvjson
來自csvkit將 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 行都有一個對象。此表達式從每個物件中jq
挑選出第五 ( ) 列並提取其中的每個單字。使用的函數e
將每個單字的第一個字元轉換為大寫,然後將結果輸出為正確引用的 CSV 資料。ascii_upcase
jq
鑑於問題中的數據,這將導致
1,,,,"Ivan Petrov",,67,
2,2,,,"Vasia Pupkin","director",8,
3,,,,"John Lenon",,,
這也可以處理包含嵌入逗號和換行符的 CSV 欄位。