我有一個包含以下資料的檔案:
GS*PO*112233*445566*20211006*155007*2010408*X*004010~
ST*850*0001~
BEG*00*DS*A-112233**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*SO168219~
REF*DC*ABC~
ST*850*0002~
BEG*00*DS*A-44556**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*PO54361~
ST*850*0003~
BEG*00*DS*A-12345**20211005~
REF*K6*Drop Ship Order~
REF*DC*XYZ~
REF*ZZ*SO897654~
為清楚起見,我在每ST*850行上方插入了空行。這是我想要做的:
- 搜索模式
REF*ZZ*SO - 如果找到,則將前
ST*850一行替換為ST*850C
因此生成的檔案將如下所示:
GS*PO*112233*445566*20211006*155007*2010408*X*004010~
ST*850C*0001~
BEG*00*DS*A-112233**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*SO168219~
REF*DC*ABC~
ST*850*0002~
BEG*00*DS*A-44556**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*PO54361~
ST*850C*0003~
BEG*00*DS*A-12345**20211005~
REF*K6*Drop Ship Order~
REF*DC*XYZ~
REF*ZZ*SO897654~
這是我嘗試過的:
sed -i -n '/^REF\*ZZ\*SO/!{x;s/ST\*850\*/ST\*850C\*/;x};x;1!p;${x;p}' file
這將替換所有ST*850三行,ST*850C而不僅僅是第一行和第三行。我究竟做錯了什么?
uj5u.com熱心網友回復:
perl盡管 perl 未包含在標簽中,但解決方案如何。
perl -0777 -aF'(?=ST\*850)' -ne '
print map {/REF\*ZZ\*SO/ && s/ST\*850/$&C/; $_} @F;
' file
輸出:
GS*PO*112233*445566*20211006*155007*2010408*X*004010~
ST*850C*0001~
BEG*00*DS*A-112233**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*SO168219~
REF*DC*ABC~
ST*850*0002~
BEG*00*DS*A-44556**20211005~
REF*K6*Drop Ship Order~
REF*ZZ*PO54361~
ST*850C*0003~
BEG*00*DS*A-12345**20211005~
REF*K6*Drop Ship Order~
REF*DC*XYZ~
REF*ZZ*SO897654~
- 該
-0777選項告訴perl您一次吞食整個檔案。 - 該
-a選項啟用auto split模式,然后拆分片段存盤在陣列中@F。 - 該
-F選項指定拆分輸入的模式。 - 正則運算式
(?=ST\*850)是一個積極的后視,它在字串的開頭匹配ST*850。 - 該
-ne選項主要等同于sed. - 該
map {..} @F函式@F根據大括號內的陳述句轉換所有元素。 - 該陳述句
/REF\*ZZ\*SO/ && s/ST\*850/$&C/翻譯為:“如果@F 的元素與模式 /REF*ZZ*SO/ 匹配,則對該元素執行替換 s/ST*850/$&C/。” - 最后一個
$_是類似于pattern spacesed的 perl 的默認變數,將是 map 函式的回傳值。
uj5u.com熱心網友回復:
這可能對你有用(GNU sed):
sed '/ST\*850/{:a;/REF\*ZZ\*SO/!{N;ba};s/.*ST\*850/&C/}' file
如果一行包含 ,則開始收集行ST*850。
在匹配包含REF*ZZ*SO使用貪婪的行追加C到最新的ST*850字串。
注意正則運算式.*確保匹配將從集合的末尾而不是集合的開始回溯。
uj5u.com熱心網友回復:
預處理sed以插入換行符,然后將每個塊視為一條awk記錄,例如:
sed 's/^ST\*850/\n&/' | awk '/REF\*ZZ\*SO/ { sub(/ST\*850/, "&C") } 1' RS=
uj5u.com熱心網友回復:
假設ST本質上是一個記錄分隔符,你可以使用一個簡單的awk腳本來收集當前記錄中的行,如果條件合適,列印一個修改過的不同的。
awk 'BEGIN { ORS = RS = "\nST" }
/REF\*ZZ\*SO/ { sub(/^\*850/, "*<850C") }1' filename
該BEGIN子句將記錄分隔符 ( RS) 和輸出記錄分隔符 ( ORS) 設定為ST以換行符開頭的字串。(嘗試包含星號變得很復雜,所以我避免這樣做。)最后1是“列印到達此處的所有內容”的常見 Awk 速記。
sed除了簡單的基于行的替換之外,對于任何事情來說都相當笨拙;我想你會發現切換到更高級的語言會提高可維護性。
uj5u.com熱心網友回復:
Pure Bash:更加冗長,但希望不需要任何額外的解釋。
#! /bin/bash
init_chunk()
{
prefix=$1
suffix=$2
chunk=()
refzzso=
}
print_chunk()
{
if [[ ${#chunk[@]} > 0 ]]; then
if [[ $refzzso == true ]]; then
printf '%sC%s\n' "$prefix" "$suffix"
else
printf '%s%s\n' "$prefix" "$suffix"
fi
printf '%s\n' "${chunk[@]}"
fi
}
init_chunk
while read -r line; do
# Check for header.
if [[ $line =~ ^(ST\*850)(.*) ]]; then
# Print previous chunk.
print_chunk
# Begin new chunk.
init_chunk "${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
continue
fi
# Check if in a chunk.
if [[ $prefix ]]; then
# Check for modifier.
if [[ $line =~ ^REF\*ZZ\*SO ]]; then
refzzso=true
fi
chunk =("$line")
else
printf '%s\n' "$line"
fi
done
# Print last chunk.
print_chunk
uj5u.com熱心網友回復:
您的解決方案替換所有出現的原因是您沒有附加行,您只是在模式和保持空間之間來回交換。您需要的是一種緩沖,直到遇到一個或另一個特殊行。這通常通過將模式空間附加到保持空間直到滿足條件來完成。
使用sed(使用 GNU 測驗sed):
sed -n '/^ST\*850\*/{x;1!p;b};
/^REF\*ZZ\*SO/{1!{H;x};s/ST\*850\*/ST*850C*/;p;b};
1{h;b};H;${x;p}' file
- 如果是
ST*850*一行,交換模式并保留空格。然后,如果它不是第一行,則列印。開始新的回圈。保持空間包含ST*850*行。之前存盤在保持空間中的行(如果有)已列印。 - 否則,如果它是
REF*ZZ*SO一行,交換模式并保留空格并進行替換。然后,如果它不是第一行,則列印。開始新的回圈。保持空間包含REF*ZZ*SO行。先前存盤在保持空間中的行(如果有)已列印(修改后)。 - 否則,如果它是第一行,則用模式空間替換保持空間并開始新的回圈。因此,保持空間包含第一行。
- 否則將模式空間附加到保持空間。如果是最后一行交換模式并保持空格并列印。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/354118.html
