[英]Issue with using While loop in bash script (to split a file into multiple files)
我需要從一個文件中讀取數據並插入到多個文件中(每個文件的大小小於 3mb,文件大小可以不同)。 重要的是 - 不應將 Agent 的記錄拆分到多個文件中。 我在 UNIX bash 腳本的 While 循環中執行所有這些操作。
Input.csv
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3
Desired Output -
Output1.csv (less than 3MB)
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
Output2.csv (less than 3MB)
Src,AgentNum,PhoneNum
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3
Bash Shell 腳本
#!/bin/bash
BaseFileName=$(basename $FileName | cut -d. -f1)
Header=`head -1 $FileName`
MaxFileSize=$(( 3 * 1024 * 1024 ))
sed 1d $FileName |
while read -r line
do
echo $line >> ${BaseFileName}_${FileSeq}.csv
MatchCount=`echo $line | grep -c -E '^.DWH'`
if [[ $MatchCount -eq 1 ]]
then
FileSizeBytes=`du -b ${BaseFileName}_${FileSeq}.csv | cut -f1`
if [[ $FileSizeBytes -gt $MaxFileSize ]]
then
#Add a header record to each file
sed -i "1i ${Header}" ${BaseFileName}_${FileSeq}.csv
FileSeq=$((FileSeq + 1))
fi
fi
done
它幾乎可以正常工作,除了 1) 沒有按預期拆分記錄(代理的某些記錄拆分到多個文件中)2) 僅為第一個輸出文件插入標頭記錄。 3) 太慢了,一個 10MB 的文件需要 3 分鍾。 實際上我有一個 3GB 的文件。
有人可以建議我哪里做錯了。 有沒有更好的方法來處理這個問題?
這是一個粗略的嘗試——它不像純awk
解決方案那么快,但它比你已經擁有的要快得多:
#!/bin/bash
# two external parameters: input file name, and max size in bytes (default to 3MB)
InputFile=$1
MaxFileSize=${2:-$(( 3 * 1024 * 1024 ))}
BaseName=${InputFile%.*} # strip extension
Ext=${InputFile##*.} # store extension
FileSeq=0 # start output file at sequence 0
# redirect stdin from the input file, stdout to the first output file
exec <"$InputFile" || exit
exec >"${BaseName}.${FileSeq}.${Ext}" || exit
# read the header; copy it to the first output file, and initialize CurFileSize
IFS= read -r Header || exit
printf '%s\n' "$Header" || exit
CurFileSize=$(( ${#Header} + 1 ))
# ...then loop over our inputs, and copy appropriately
while IFS= read -r line; do
if [[ $line = DWH,* ]] && (( CurFileSize > MaxFileSize )); then
(( FileSeq++ ))
exec >"${BaseName}.${FileSeq}.${Ext}" || exit
printf '%s\n' "$Header" || exit
CurFileSize=$(( ${#Header} + 1 ))
fi
printf '%s\n' "$line" || exit
(( CurFileSize += ${#line} + 1 ))
done
值得注意的變化:
sed
,沒有basename
,沒有du
,沒有grep
。 任何時候你寫$()
或``
,都會有一個非常重要的性能成本; 除非無法避免,否則不應在緊密循環中使用這些構造——並且當使用 POSIX sh 標准的 ksh 或 bash 擴展時,它們實際上是不可能避免的。>>"$filename"
,而是在每次需要開始一個新的輸出文件時使用exec >"$filename"
。*
;用空格替換制表符;等等)。 如有疑問,請多引用。printf '%s\\n'
是由POSIX標准優於定義echo
-見為標准清晰度echo
,特別是實際應用信息部分。set -e
,但它的使用有很多注意事項。測試過程和輸出如下:
$ cat >input.csv <<'EOF'
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3
EOF
$ ./splitCSV input.csv 100 ## split at first boundary after 100 bytes
$ cat input.0.csv
Src,AgentNum,PhoneNum
DWH,Agent_1234,phone1
NULL,NULL,phone2
NULL,NULL,phone3
DWH,Agent_5678,phone1
NULL,NULL,phone2
NULL,NULL,phone3
$ cat input.1.csv
Src,AgentNum,PhoneNum
DWH,Agent_9999,phone1
NULL,NULL,phone2
NULL,NULL,phone3
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.