您好我正在嘗試以 csv 格式從下面的檔案中提取兩個欄位。
dn: lorem ipsum
PersonalId: 123456
Contacts: { big json which may contain "uniquetag:98765432:" within some attributes }
dn: lorem ipsum
PersonalId: 123456
Contacts: { big json which may contain "uniquetag:98765432:" within some attributes }
dn: lorem ipsum
PersonalId: 123456
Contacts: { big json which may contain "uniquetag:98765432:" within some attributes }
dn: lorem ipsum
PersonalId: 123456
Contacts: { big json which may contain "uniquetag:98765432:" within some attributes }
Lorem ipsum
Lorem ipsum
預期產出
123456,98765432
123456,98765432
...
該PersonalId欄位將始終為 6 位,后面的手機號碼uniquetag將始終為 8 位。如果記錄的手機號碼不存在,那么我們需要跳過該記錄。
我可以在一個簡單的 shell 腳本中執行此操作,在該腳本中逐行查找記錄,然后使用 regex: 捕獲值^.*PersonalId.*([0-9]{6}).*uniquetag:([0-9]{8}).*$。它可以作業并提供我需要的輸出。但是對于大約 200 萬條記錄(300 Mb 檔案大小),處理需要將近一個小時。需要在更大的檔案大小上運行它。我做了一些優化,例如將原始檔案拆分為多個較小的檔案并并行處理它們。但它并沒有提高性能。
我覺得我做的事情非常錯誤。可以直接使用 awk 或 sed 來提取這些欄位。有沒有辦法直接使用 awk 或 sed 來做到這一點?
我擁有的當前 shell 腳本
#!/bin/sh
start=$(date %s)
_input_file="$1"
_output_file="$2"
_parallel_threads="$3"
_debug_enabled="$4"
_user_regex='^.*PersonalId.*([0-9]{6}).*uniquetag:([0-9]{8}).*$'
debug_log() {
log_message="$1"
if [[ $_debug_enabled == debug* ]]; then
printf "%b\n" "$log_message"
fi
}
process_user_record() {
user_record="$1"
output_file_name="$2"
debug_log "processing user record"
record=$(echo "$user_record" | tr -d '\n')
debug_log "$record"
[[ $record =~ $_user_regex ]] && debug_log "user record matched regex" || debug_log "no match"
match_count=${#BASH_REMATCH[@]}
debug_log "Match count is $match_count"
if [[ $match_count -gt 2 ]]; then
pid="${BASH_REMATCH[1]}"
mobile="${BASH_REMATCH[2]}"
debug_log "Writing $pid and $mobile to output"
echo "$pid,$mobile" >>$output_file_name
fi
}
process_record_file() {
record_file_name="$1"
output_file="output_files/${record_file_name##*/}"
user_data=''
touch "$output_file"
debug_log "processing: $record_file_name"
while IFS= read -r line; do
if [[ $line == dn* ]]; then
debug_log 'line matches with dn'
if [ ! -z "$user_data" ]; then
process_user_record "$user_data" "$output_file"
user_data=''
fi
else
debug_log "Appending to user data"
user_data="${user_data}\n${line}"
fi
done <"$record_file_name"
if [ ! -z "$user_data" ]; then
process_user_record "$user_data" "$output_file"
user_data=''
fi
}
echo "pid,mobile" >$_output_file
debug_log 'Starting export'
mkdir 'input_files_split'
mkdir 'output_files'
awk -v max=1000 '{print > sprintf("input_files_split/recordd", int(n/max))} /^$/ {n = 1}' "$_input_file"
declare -i counter=0
for file in input_files_split/*; do
if [[ $counter -ge $_parallel_threads ]]; then
wait
counter=0
fi
process_record_file "$file" &
counter =1
done
wait
cat output_files/* >>$_output_file
rm -rf input_files_split/
rm -rf output_files/
end=$(date %s)
runtime=$((end - start))
echo "Export ready\nTime taken: ${runtime}s"
uj5u.com熱心網友回復:
使用sed
$ sed -n '/PersonalId:/{N;/uniquetag:/s/[^:]*: \(.*\)\n.*uniquetag:\([^:]*\).*/\1,\2/p}' input_file
123456,98765432
123456,98765432
123456,98765432
123456,98765432
匹配以 . 開頭的行PersonalID。如果下一行匹配uniquetag:,則在捕獲括號內捕獲所需資料,并以反斜杠回傳\1。
uj5u.com熱心網友回復:
awk -F: '/^PersonalId/{ gsub(" ",""); id=$2; getline; if($3!="") print id","$3 }' csv_file
uj5u.com熱心網友回復:
{m,g}awk '
ORS = substr("\n,", NF< _?!_ :(NF== _) !!_,( _<= NF)~($!NF = $ _)^!_))'\
\_=2 OFS= FS='^(PersonalId|. uniquetag)[:][ ]?|[:].*[}].*$'
123456,98765432
123456,98765432
123456,98765432
123456,98765432
uj5u.com熱心網友回復:
這可能對您有用(GNU sed):
sed -nE '/PersonalId:/h
/uniquetag:/{
H;g;s/.*PersonalId: *([0-9]{6}).*uniquetag:([0-9]{8}).*/\1,\2/p;}' file
將PersonalId資訊復制到保留空間。
將資訊附加uniquetag到PersonalId資訊并相應地格式化。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/491444.html
上一篇:kornshell中的例外處理
下一篇:函式內的Bash腳本變數范圍
