Swap lines in text file only where containing strings using sed or ed?
Clash Royale CLAN TAG#URR8PPP
up vote
3
down vote
favorite
I need to swap these lines, only where the matching strings are in both lines:
before:
REF*CE*-------------------------
REF*1W*-------------------------
after:
REF*1W*-------------------------
REF*CE*-------------------------
I tried this, it didn't work:
ed -s testfile.txt <<<$'/REF*CE*/-0,/REF*CE*/+0m/REF*1W*/nwnq'
text-processing sed ed
 |Â
show 1 more comment
up vote
3
down vote
favorite
I need to swap these lines, only where the matching strings are in both lines:
before:
REF*CE*-------------------------
REF*1W*-------------------------
after:
REF*1W*-------------------------
REF*CE*-------------------------
I tried this, it didn't work:
ed -s testfile.txt <<<$'/REF*CE*/-0,/REF*CE*/+0m/REF*1W*/nwnq'
text-processing sed ed
the matching strings - by the prefixREF
?
â RomanPerekhrest
Jun 28 '17 at 18:24
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
1
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15
 |Â
show 1 more comment
up vote
3
down vote
favorite
up vote
3
down vote
favorite
I need to swap these lines, only where the matching strings are in both lines:
before:
REF*CE*-------------------------
REF*1W*-------------------------
after:
REF*1W*-------------------------
REF*CE*-------------------------
I tried this, it didn't work:
ed -s testfile.txt <<<$'/REF*CE*/-0,/REF*CE*/+0m/REF*1W*/nwnq'
text-processing sed ed
I need to swap these lines, only where the matching strings are in both lines:
before:
REF*CE*-------------------------
REF*1W*-------------------------
after:
REF*1W*-------------------------
REF*CE*-------------------------
I tried this, it didn't work:
ed -s testfile.txt <<<$'/REF*CE*/-0,/REF*CE*/+0m/REF*1W*/nwnq'
text-processing sed ed
text-processing sed ed
edited Sep 6 '17 at 9:36
Kusalananda
114k15217347
114k15217347
asked Jun 28 '17 at 18:22
user5586678
213
213
the matching strings - by the prefixREF
?
â RomanPerekhrest
Jun 28 '17 at 18:24
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
1
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15
 |Â
show 1 more comment
the matching strings - by the prefixREF
?
â RomanPerekhrest
Jun 28 '17 at 18:24
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
1
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15
the matching strings - by the prefix
REF
?â RomanPerekhrest
Jun 28 '17 at 18:24
the matching strings - by the prefix
REF
?â RomanPerekhrest
Jun 28 '17 at 18:24
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
1
1
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15
 |Â
show 1 more comment
4 Answers
4
active
oldest
votes
up vote
1
down vote
sed -e :a -e '$!N;s/^(REF*CE.*)n(REF*1W.*)/2n1/;ta' -e 'P;D' <testfile.txt
- If we're not on the last line then append next line.
- Do a substitution on the current line that only occurs if it matches
substring containing pattern 1 + newline + substring containing pattern 2
. The substitution flips the two sub-strings. After substitution go back to label :a. - If there was no match Print the pattern space as is. Then Delete the pattern space and start the cycle again.
Sample with some surrounding lines...
In:
XEF*CE*-------------------------
REF*CE*-------------------------
REF*1W*-------------------------
REF*2W*-------------------------
Out:
XEF*CE*-------------------------
REF*1W*-------------------------
REF*CE*-------------------------
REF*2W*-------------------------
More generally for any pattern1 and pattern2
sed -e :a
-e "$!N; s/^(.*$pattern1.*)n(.*$pattern2.*)/2n1/;ta"
-e 'P;D' < inputfile
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
add a comment |Â
up vote
0
down vote
We can get the desired effect by putting the matched line into hold buffer, reading next line and printing it, then swapping the hold buffer with pattern buffer and printing again.
bash-4.3$ sed -n '/^REF*CE/!p;/^REF*CE/h;n;p;x;p' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
 |Â
show 8 more comments
up vote
0
down vote
A general solution for how to swap two (possibly distant) lines that match particular regular expressions with ed
:
Copy one line to after the second line.
Move the second line to after the original first line.
Delete the first original line.
Or, with ed
editing commands:
/pat1/t/pat2/
?pat2?m/pat1/
?pat1?d
Example with the file
CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
in which we'd like to swap the first AMT
line with the second CAS
line. pat1
will be ^AMT*AU
and pat2
will be ^CAS*PR
. Note that we need to escape the *
for it to be treated literally in the regular expression.
I have annotated the changes below so that it's easier to see them. The XXX
indicates the current position in the file after each operation.
/^AMT*AU/t/^CAS*PR/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8 <-- Line copied *from* here
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
AMT*AU*489.8 <-- Line copied *to* here (XXX)
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^CAS*PR?m/^AMT*AU/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
CAS*PR*2*82.29 <-- line moved here (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8 <-- line previous to this deleted
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^AMT*AU?d
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
CAS*PR*2*82.29 <-- the line before this was removed (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
As an easy-to-remember "one-liner":
pat1='^AMT*AU'; pat2='^CAS*PR'; printf '/%s/t/%s/n?%s?m/%s/n?%s?dnwqn' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file
Note that this is reversible by running the exact thing a second time, i.e. it doesn't matter which pattern is the first or second.
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up ifpat1
occurs many times. It would be best to try to have as specific patterns as possible.
â Kusalananda
Sep 7 '17 at 12:02
add a comment |Â
up vote
-1
down vote
I have simple solution like this:
Say, If you want swap a line that has "XXXX" with line that has "YYYY" in afile, providing only one line has "XXXX" and only one has "YYYY"
Example file is like this:
*ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh
My 'sed' command is sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g'
Remember "ZZZZ" string should not be in the file anyware.
New contributor
add a comment |Â
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
sed -e :a -e '$!N;s/^(REF*CE.*)n(REF*1W.*)/2n1/;ta' -e 'P;D' <testfile.txt
- If we're not on the last line then append next line.
- Do a substitution on the current line that only occurs if it matches
substring containing pattern 1 + newline + substring containing pattern 2
. The substitution flips the two sub-strings. After substitution go back to label :a. - If there was no match Print the pattern space as is. Then Delete the pattern space and start the cycle again.
Sample with some surrounding lines...
In:
XEF*CE*-------------------------
REF*CE*-------------------------
REF*1W*-------------------------
REF*2W*-------------------------
Out:
XEF*CE*-------------------------
REF*1W*-------------------------
REF*CE*-------------------------
REF*2W*-------------------------
More generally for any pattern1 and pattern2
sed -e :a
-e "$!N; s/^(.*$pattern1.*)n(.*$pattern2.*)/2n1/;ta"
-e 'P;D' < inputfile
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
add a comment |Â
up vote
1
down vote
sed -e :a -e '$!N;s/^(REF*CE.*)n(REF*1W.*)/2n1/;ta' -e 'P;D' <testfile.txt
- If we're not on the last line then append next line.
- Do a substitution on the current line that only occurs if it matches
substring containing pattern 1 + newline + substring containing pattern 2
. The substitution flips the two sub-strings. After substitution go back to label :a. - If there was no match Print the pattern space as is. Then Delete the pattern space and start the cycle again.
Sample with some surrounding lines...
In:
XEF*CE*-------------------------
REF*CE*-------------------------
REF*1W*-------------------------
REF*2W*-------------------------
Out:
XEF*CE*-------------------------
REF*1W*-------------------------
REF*CE*-------------------------
REF*2W*-------------------------
More generally for any pattern1 and pattern2
sed -e :a
-e "$!N; s/^(.*$pattern1.*)n(.*$pattern2.*)/2n1/;ta"
-e 'P;D' < inputfile
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
add a comment |Â
up vote
1
down vote
up vote
1
down vote
sed -e :a -e '$!N;s/^(REF*CE.*)n(REF*1W.*)/2n1/;ta' -e 'P;D' <testfile.txt
- If we're not on the last line then append next line.
- Do a substitution on the current line that only occurs if it matches
substring containing pattern 1 + newline + substring containing pattern 2
. The substitution flips the two sub-strings. After substitution go back to label :a. - If there was no match Print the pattern space as is. Then Delete the pattern space and start the cycle again.
Sample with some surrounding lines...
In:
XEF*CE*-------------------------
REF*CE*-------------------------
REF*1W*-------------------------
REF*2W*-------------------------
Out:
XEF*CE*-------------------------
REF*1W*-------------------------
REF*CE*-------------------------
REF*2W*-------------------------
More generally for any pattern1 and pattern2
sed -e :a
-e "$!N; s/^(.*$pattern1.*)n(.*$pattern2.*)/2n1/;ta"
-e 'P;D' < inputfile
sed -e :a -e '$!N;s/^(REF*CE.*)n(REF*1W.*)/2n1/;ta' -e 'P;D' <testfile.txt
- If we're not on the last line then append next line.
- Do a substitution on the current line that only occurs if it matches
substring containing pattern 1 + newline + substring containing pattern 2
. The substitution flips the two sub-strings. After substitution go back to label :a. - If there was no match Print the pattern space as is. Then Delete the pattern space and start the cycle again.
Sample with some surrounding lines...
In:
XEF*CE*-------------------------
REF*CE*-------------------------
REF*1W*-------------------------
REF*2W*-------------------------
Out:
XEF*CE*-------------------------
REF*1W*-------------------------
REF*CE*-------------------------
REF*2W*-------------------------
More generally for any pattern1 and pattern2
sed -e :a
-e "$!N; s/^(.*$pattern1.*)n(.*$pattern2.*)/2n1/;ta"
-e 'P;D' < inputfile
edited Jul 1 '17 at 0:28
answered Jun 30 '17 at 10:48
B Layer
3,9541525
3,9541525
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
add a comment |Â
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:
REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
I tried the sed command on my test file, and it didn't work. Here is the relevant section of the file:
REF*BB*106497026~
REF*CE*NEW JERSEY~
REF*1W*723266637~
SVC*HC^S5102*78.5*78.5**1~
â user5586678
Jun 30 '17 at 13:59
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
Sorry, I should have tested a bit more. Hopefully this does the trick. Your testcases work for me.
â B Layer
Jun 30 '17 at 22:10
add a comment |Â
up vote
0
down vote
We can get the desired effect by putting the matched line into hold buffer, reading next line and printing it, then swapping the hold buffer with pattern buffer and printing again.
bash-4.3$ sed -n '/^REF*CE/!p;/^REF*CE/h;n;p;x;p' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
 |Â
show 8 more comments
up vote
0
down vote
We can get the desired effect by putting the matched line into hold buffer, reading next line and printing it, then swapping the hold buffer with pattern buffer and printing again.
bash-4.3$ sed -n '/^REF*CE/!p;/^REF*CE/h;n;p;x;p' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
 |Â
show 8 more comments
up vote
0
down vote
up vote
0
down vote
We can get the desired effect by putting the matched line into hold buffer, reading next line and printing it, then swapping the hold buffer with pattern buffer and printing again.
bash-4.3$ sed -n '/^REF*CE/!p;/^REF*CE/h;n;p;x;p' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there
We can get the desired effect by putting the matched line into hold buffer, reading next line and printing it, then swapping the hold buffer with pattern buffer and printing again.
bash-4.3$ sed -n '/^REF*CE/!p;/^REF*CE/h;n;p;x;p' input.txt
some line here
REF*BB*106497026---------------
REF*1W*723266637---------------
REF*CE*NEW JERSEY--------------
SVC*HC^S5102*78.5*78.5**1------
another line there
edited Jun 30 '17 at 22:20
answered Jun 30 '17 at 22:10
Sergiy Kolodyazhnyy
8,02011951
8,02011951
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
 |Â
show 8 more comments
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
Doesn't work with the case cited in the first comment below my answer.
â B Layer
Jun 30 '17 at 22:15
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
@BlairM. I just tested with the example OP mentioned. Works OK. Can you clarify what exactly doesn't work ?
â Sergiy Kolodyazhnyy
Jun 30 '17 at 22:21
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
As don_crissti mentioned try it where the first pattern repeats on consecutive lines. Mine didn't work with this case until I updated it. Yours seems to discard a line at some point.
â B Layer
Jun 30 '17 at 23:34
1
1
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
I would imagine that OP is not trying to solve only for the very specific case that he supplied and his comment to my answer suggests as much. Regardless I want my answer to be robust enough to work generally, matching consecutive patterned lines under any circumstance, so that other people may find it useful.
â B Layer
Jul 1 '17 at 0:17
1
1
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
@user5586678 I'm out, man, sorry. You're not paying attention to what anyone is saying ("add better examples of input/output to your question", "explain what didn't work with Blair M's answer", "please reply under my answer, not here", etc.) and I don't have infinite time to spend on this.
â B Layer
Jul 8 '17 at 0:52
 |Â
show 8 more comments
up vote
0
down vote
A general solution for how to swap two (possibly distant) lines that match particular regular expressions with ed
:
Copy one line to after the second line.
Move the second line to after the original first line.
Delete the first original line.
Or, with ed
editing commands:
/pat1/t/pat2/
?pat2?m/pat1/
?pat1?d
Example with the file
CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
in which we'd like to swap the first AMT
line with the second CAS
line. pat1
will be ^AMT*AU
and pat2
will be ^CAS*PR
. Note that we need to escape the *
for it to be treated literally in the regular expression.
I have annotated the changes below so that it's easier to see them. The XXX
indicates the current position in the file after each operation.
/^AMT*AU/t/^CAS*PR/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8 <-- Line copied *from* here
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
AMT*AU*489.8 <-- Line copied *to* here (XXX)
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^CAS*PR?m/^AMT*AU/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
CAS*PR*2*82.29 <-- line moved here (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8 <-- line previous to this deleted
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^AMT*AU?d
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
CAS*PR*2*82.29 <-- the line before this was removed (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
As an easy-to-remember "one-liner":
pat1='^AMT*AU'; pat2='^CAS*PR'; printf '/%s/t/%s/n?%s?m/%s/n?%s?dnwqn' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file
Note that this is reversible by running the exact thing a second time, i.e. it doesn't matter which pattern is the first or second.
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up ifpat1
occurs many times. It would be best to try to have as specific patterns as possible.
â Kusalananda
Sep 7 '17 at 12:02
add a comment |Â
up vote
0
down vote
A general solution for how to swap two (possibly distant) lines that match particular regular expressions with ed
:
Copy one line to after the second line.
Move the second line to after the original first line.
Delete the first original line.
Or, with ed
editing commands:
/pat1/t/pat2/
?pat2?m/pat1/
?pat1?d
Example with the file
CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
in which we'd like to swap the first AMT
line with the second CAS
line. pat1
will be ^AMT*AU
and pat2
will be ^CAS*PR
. Note that we need to escape the *
for it to be treated literally in the regular expression.
I have annotated the changes below so that it's easier to see them. The XXX
indicates the current position in the file after each operation.
/^AMT*AU/t/^CAS*PR/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8 <-- Line copied *from* here
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
AMT*AU*489.8 <-- Line copied *to* here (XXX)
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^CAS*PR?m/^AMT*AU/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
CAS*PR*2*82.29 <-- line moved here (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8 <-- line previous to this deleted
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^AMT*AU?d
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
CAS*PR*2*82.29 <-- the line before this was removed (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
As an easy-to-remember "one-liner":
pat1='^AMT*AU'; pat2='^CAS*PR'; printf '/%s/t/%s/n?%s?m/%s/n?%s?dnwqn' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file
Note that this is reversible by running the exact thing a second time, i.e. it doesn't matter which pattern is the first or second.
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up ifpat1
occurs many times. It would be best to try to have as specific patterns as possible.
â Kusalananda
Sep 7 '17 at 12:02
add a comment |Â
up vote
0
down vote
up vote
0
down vote
A general solution for how to swap two (possibly distant) lines that match particular regular expressions with ed
:
Copy one line to after the second line.
Move the second line to after the original first line.
Delete the first original line.
Or, with ed
editing commands:
/pat1/t/pat2/
?pat2?m/pat1/
?pat1?d
Example with the file
CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
in which we'd like to swap the first AMT
line with the second CAS
line. pat1
will be ^AMT*AU
and pat2
will be ^CAS*PR
. Note that we need to escape the *
for it to be treated literally in the regular expression.
I have annotated the changes below so that it's easier to see them. The XXX
indicates the current position in the file after each operation.
/^AMT*AU/t/^CAS*PR/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8 <-- Line copied *from* here
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
AMT*AU*489.8 <-- Line copied *to* here (XXX)
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^CAS*PR?m/^AMT*AU/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
CAS*PR*2*82.29 <-- line moved here (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8 <-- line previous to this deleted
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^AMT*AU?d
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
CAS*PR*2*82.29 <-- the line before this was removed (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
As an easy-to-remember "one-liner":
pat1='^AMT*AU'; pat2='^CAS*PR'; printf '/%s/t/%s/n?%s?m/%s/n?%s?dnwqn' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file
Note that this is reversible by running the exact thing a second time, i.e. it doesn't matter which pattern is the first or second.
A general solution for how to swap two (possibly distant) lines that match particular regular expressions with ed
:
Copy one line to after the second line.
Move the second line to after the original first line.
Delete the first original line.
Or, with ed
editing commands:
/pat1/t/pat2/
?pat2?m/pat1/
?pat1?d
Example with the file
CLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
in which we'd like to swap the first AMT
line with the second CAS
line. pat1
will be ^AMT*AU
and pat2
will be ^CAS*PR
. Note that we need to escape the *
for it to be treated literally in the regular expression.
I have annotated the changes below so that it's easier to see them. The XXX
indicates the current position in the file after each operation.
/^AMT*AU/t/^CAS*PR/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8 <-- Line copied *from* here
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
CAS*PR*2*82.29
AMT*AU*489.8 <-- Line copied *to* here (XXX)
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^CAS*PR?m/^AMT*AU/
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
AMT*AU*489.8
CAS*PR*2*82.29 <-- line moved here (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8 <-- line previous to this deleted
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43?^AMT*AU?d
producesCLP*815900102*2*489.8*101.5*82.29*13*PVJLS03YP0000*13*7
CAS*PR*2*82.29 <-- the line before this was removed (XXX)
REF*6R*00000000002
DTM*472*20160528
CAS*OA*23*306.01
AMT*AU*489.8
SVC*HC:99212:25*489.8*101.5**1
AMT*B6*411.43
As an easy-to-remember "one-liner":
pat1='^AMT*AU'; pat2='^CAS*PR'; printf '/%s/t/%s/n?%s?m/%s/n?%s?dnwqn' "$pat1" "$pat2" "$pat2" "$pat1" "$pat1" | ed -s file
Note that this is reversible by running the exact thing a second time, i.e. it doesn't matter which pattern is the first or second.
edited Sep 6 '17 at 9:48
answered Sep 6 '17 at 9:30
Kusalananda
114k15217347
114k15217347
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up ifpat1
occurs many times. It would be best to try to have as specific patterns as possible.
â Kusalananda
Sep 7 '17 at 12:02
add a comment |Â
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up ifpat1
occurs many times. It would be best to try to have as specific patterns as possible.
â Kusalananda
Sep 7 '17 at 12:02
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
Kusalananda, does this work if the pattern repeats multiple times in the file?
â user5586678
Sep 7 '17 at 11:52
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 It will pick the first occurrence of each pattern.
â Kusalananda
Sep 7 '17 at 12:00
@user5586678 Actually, the second step may become seriously messed up if
pat1
occurs many times. It would be best to try to have as specific patterns as possible.â Kusalananda
Sep 7 '17 at 12:02
@user5586678 Actually, the second step may become seriously messed up if
pat1
occurs many times. It would be best to try to have as specific patterns as possible.â Kusalananda
Sep 7 '17 at 12:02
add a comment |Â
up vote
-1
down vote
I have simple solution like this:
Say, If you want swap a line that has "XXXX" with line that has "YYYY" in afile, providing only one line has "XXXX" and only one has "YYYY"
Example file is like this:
*ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh
My 'sed' command is sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g'
Remember "ZZZZ" string should not be in the file anyware.
New contributor
add a comment |Â
up vote
-1
down vote
I have simple solution like this:
Say, If you want swap a line that has "XXXX" with line that has "YYYY" in afile, providing only one line has "XXXX" and only one has "YYYY"
Example file is like this:
*ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh
My 'sed' command is sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g'
Remember "ZZZZ" string should not be in the file anyware.
New contributor
add a comment |Â
up vote
-1
down vote
up vote
-1
down vote
I have simple solution like this:
Say, If you want swap a line that has "XXXX" with line that has "YYYY" in afile, providing only one line has "XXXX" and only one has "YYYY"
Example file is like this:
*ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh
My 'sed' command is sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g'
Remember "ZZZZ" string should not be in the file anyware.
New contributor
I have simple solution like this:
Say, If you want swap a line that has "XXXX" with line that has "YYYY" in afile, providing only one line has "XXXX" and only one has "YYYY"
Example file is like this:
*ssss dddd
aaaa ffff
ddd rrrr
ddddd XXXX
ddddde
ffff
ffff
fff
eeee YYYY
ghghgh
hhhhh
My 'sed' command is sed 's/XXXX/ZZZZ/g;s/YYYY/XXXX/g;s/ZZZZ/YYYY/g'
Remember "ZZZZ" string should not be in the file anyware.
New contributor
New contributor
answered 24 mins ago
Sarath Anura Hiyare Hewage
1
1
New contributor
New contributor
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f373996%2fswap-lines-in-text-file-only-where-containing-strings-using-sed-or-ed%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
the matching strings - by the prefix
REF
?â RomanPerekhrest
Jun 28 '17 at 18:24
REFCE and REF*1W* are matching strings
â user5586678
Jun 28 '17 at 18:27
Literal asterisks, or RE followed by zero or more Fs, followed by C, followed by zero or more Es?
â DopeGhoti
Jun 28 '17 at 19:44
yes, if both patterns match, swap lines
â user5586678
Jun 29 '17 at 19:39
1
Does your file contain other lines also? Are the given lines adjacent? How should non-adjacent REF lines be handled? Might your file contain more than two lines with the pattern REF? Should more pairs be swapped? What if there are an odd number of REF lines?
â Wildcard
Jun 30 '17 at 22:15