I have a file that contains the following data:
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~
For clarity, I have inserted blank line above each ST*850 line. Here is what I want to do:
REF*ZZ*SOST*850 line with ST*850CSo the resultant file would look like this:
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~
Here is what I have tried:
sed -i -n '/^REF\*ZZ\*SO/!{x;s/ST\*850\*/ST\*850C\*/;x};x;1!p;${x;p}' file
This replaces all the three ST*850 lines with ST*850C and not just the 1st and the 3rd. What am I doing wrong?
This might work for you (GNU sed):
sed '/ST\*850/{:a;/REF\*ZZ\*SO/!{N;ba};s/.*ST\*850/&C/}' file
Begin gathering up lines if a line contains ST*850.
On matching a line that contains REF*ZZ*SO use greed to append C to the latest ST*850 string.
N.B. The regexp .* ensures that the match will backtrack from the end of the collection rather than the start of the collection.
How about a perl solution although perl is not included in the tags.
perl -0777 -aF'(?=ST\*850)' -ne '
print map {/REF\*ZZ\*SO/ && s/ST\*850/$&C/; $_} @F;
' file
Output:
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 option tells perl to slurp whole file at once.-a option enables the auto split mode then the split
fragments are stored in the array @F.-F option specifies the pattern to split the input.(?=ST\*850) is a positive lookbehind which matches
at the beginning of a string ST*850.-ne option is mostly equivalent to that of sed.map {..} @F function converts all elements of @F according
to the statement within the curly brackets./REF\*ZZ\*SO/ && s/ST\*850/$&C/ is translated as:
"if the element of @F matches the pattern /REF*ZZ*SO/, then perform
the substitution s/ST*850/$&C/ for the element."$_ is the perl's default variable similar to the pattern space of sed and will be the return values of the map function.Assuming ST is essentially a record separator, you can use a simple Awk script to collect the lines in the current record, and print a modified different one if the conditions are right.
awk 'BEGIN { ORS = RS = "\nST" }
/REF\*ZZ\*SO/ { sub(/^\*850/, "*<850C") }1' filename
The BEGIN clause sets the record separator (RS) and also the output record separator (ORS) to the string ST preceded by a newline. (Attempting to include the asterisk got complicated, so I avoided that.) The final 1 is the common Awk shorthand for "print everything which reaches here".
sed is rather unwieldy for anything beyond simple line-based substitutions; I think you will find that switching to a higher-level language is going to improve maintainability.