So this is what I used to get the source material:
cat <<F1 >/tmp/f1 ; cat <<F2 >/tmp/f2
$(for i in 1 2 3 4 5 ; do { \
printf "00%s" $i ; printf ";%s" \
abc def ghi jkl pqr ; echo ; } ; done)
F1
$(for i in 1 2 3 4 5 ; do {\
printf "00%s" $i ; echo ";mno" ; } ; done)
F2
It supplies:
001;abc;def;ghi;jkl;pqr
002;abc;def;ghi;jkl;pqr
003;abc;def;ghi;jkl;pqr
004;abc;def;ghi;jkl;pqr
005;abc;def;ghi;jkl;pqr
001;mno
002;mno
003;mno
004;mno
005;mno
I tested this in various ways, and this is the resulting command:
% sed -e 'R /tmp/f2' /tmp/f1 |\
sed -r 'N;s/(.*)(;[^;]*)\n[^;]*(.*)/\1\3\2/'
This is GNU sed
only - because GNU offers the R
function we can read in a separate file line-by-line in lockstep with our input. This means no branching and no looping. In this way, I expect sed
would work more efficiently than awk
because it wouldn't have to read the contents entirely into memory before operating and it can operate on a live stream.
I attempted to make this work without the |pipe
and second sed
invocation, but because sed
appends /tmp/f2 to its own stdout
nothing I tried allowed me to edit in stream without the |pipe
. sed
first collates the two files in stream and edits the result on the other end of the |pipe
.
Anyway, one |pipe
though and it's still streamed, but you need two sed
s . Run the above sed
command on your data and:
OUTPUT
> 001;abc;def;ghi;jkl;mno;pqr
> 002;abc;def;ghi;jkl;mno;pqr
> 003;abc;def;ghi;jkl;mno;pqr
> 004;abc;def;ghi;jkl;mno;pqr
> 005;abc;def;ghi;jkl;mno;pqr
Here's how it works:
sed -r 'N;s/(.*)(;[^;]*)\n[^;]*(.*)/\1\3\2/'
- N because we already know that
sed
is appending each successive line from f2
to those in f1
the very first thing we do after receiving a line is to pull in the N
ext one.
s having joined the two lines we need in pattern space, we begin the s
earch and replace function
001;abc;def;ghi;jkl;pqr\n001;mno
\1 (.*) first tell sed
to (
group * everything it )
finds from the left-most portion of the pattern space into the \1
back-reference until...
001;abc;def;ghi;jkl*;pqr*\n001;mno
\2 (;[^;]*)\n it encounters a string consisting of a...
- ; semicolon then ...
- [^;]* a string consisting entirely of ^no ;semicolons immediately followed by
- \n the
\n
ewline character added when we pulled in f2
's line with N
and which will be discarded
- () In this way we back reference the
(
last semicolon delimited field )
from the line in f1
to \2
[^;]* beginning with f2
's line we search through and discard all characters until we encounter a semicolon, and
001;abc;def;ghi;jkl;pqr\n001*;mno*
\3 (.*) we store everything that remains in the \3
backreference
\1\3\2 once we've split out the string as we require all that remains is to put it back together in the right order, so we insert \3
before \2
and we're through with this search replace cycle until we're fed a new line