Swapping or switching 2 lines using sed

I made a script that can swap info on two lines using a combination of awk and sed, but was hoping to consolidate the script to make it run faster. If found this script, but can't seem to get it to work in a bash shell. I keep getting the error "Too many {'s". Any help here would be appreciated:

Swapping two lines with sed
Assume, in the following file I want to swap the third with the fifth line:

one
two
five
four
three
six
seven

Then this could be done with the following sed script:

sed -ne '
 3!{p;d;}
 h;n;:1
 4!{N;b1}
 G;h;n;p;g;p' numbers

Note, it's a 4, not a 5 in the sed script!

hi

I think below perl script can help you a little.

$file=shift;
$l1=shift;
$l2=shift;
open FH,"<$file";
while(<FH>){
	$arr[$.]=$_;
}
close FH;
for($i=1;$i<=$#arr;$i++){
	if($i==$l1){
		print $arr[$l2];
	}
	else{
		if ($i==$l2){
			print $arr[$l1];
		}
		else{
			print $arr[$i];
		}
	}
}

Below sed script can not adress your question flexbily, but should throw some light on that.

1
2
5
4
3
6
1
2
3
4
5
6
sed -n '/5/ !{
p
}
/5/ {
h
n
G
x
n
G
p
}'

I'm sorry, but I'm new to unix and am not familiar with perl...in that script, where do i tell the script what file to sort...also, how do i tell it which lines to swap?

With awk:

awk 'NR==3 {
  s=$0
  getline;s=$0"\n"s
  getline;print;print s
  next
}1' file

Regards

Awesome! is there any way to make this code dynamic? I wanted to put some shell variables in here...For example, If I wanted to start with line $x instead of 3, I would start by replacing NR==3 with NR=="'"$x"'"...where would I input my variable for line 5?

You can use a variable as follow:

#!/bin/sh

line=3

awk -v var="$line" 'NR==var {
  s=$0
  getline;s=$0"\n"s
  getline;print;print s
  next
}1' file

Regards

Right, so now this code switches line "var" (which for this example is set at 3), with a line two below it...but how do I switch line "var" with say another line defined by another variable, say "var2"?...or would this code not be able to do that?...I know I probably should've asked for this caveat at the outset, my apologies.

If you also want to swap line 15 with line 17:

#!/bin/sh
 
line=3
line1=15
 
awk -v var="$line" -v var1="$line1" 'NR==var {
  s=$0
  getline;s=$0"\n"s
  getline;print;print s
  next
}
NR==var1 {
  s=$0
  getline;s=$0"\n"s
  getline;print;print s
  next
}
1' file

Regards

I understand what that does...but I was hoping to switch lines var with var1. This switches var with var+2 and var1 with var1+2...If this code isn't built for that...then don't worry about trying to modify this code to do that. I was just hoping it was easy to do using this code.

To swap line 3 with line 10:

#!/bin/sh
 
line=3
line1=10

awk -v var="$line" -v var1="$line1" 'NR==var {
  s=$0
  for(i=var+1;i<var1;i++){
    getline;s=$0"\n"s
  }
  getline;print;print s
  next
}1' file

Regards

Thanks that works perfectly!:smiley:

Another one:

(use nawk or /usr/xpg4/bin/awk on Solaris)

awk 'NR == FNR {
  if (NR ~ "^("ln1"|"ln2")$")
    _[++c] = $0
  next
  }
FNR ~ "^("ln1"|"ln2")$" { $0 = _[c--] } 
1' ln1=3 ln2=5 infile infile

or:

awk 'NR == ln1, NR == ln2 { 
  _[++c] = $0
  if (NR == ln2 && j = c) 
    while (++i <= c)
      print _[NR ~ "^("ln1"|"ln2")$"?j--:i]
  next
}1' ln1=3 ln2=5 infile

I had trouble getting either of these to work (i'm probably running it in the wrong shell)...I was able to get the latter to work by passing my variables from bash into the awk statement using "'"s...see below modified code.

#!/bin/bash
ln1=3
ln2=5
awk 'NR=="'"$ln1"'", NR=="'"$ln2"'" { 
  _[++c] = $0
  if (NR=="'"$ln2"'" && j = c) 
    while (++i<=c)
      print _[NR~"^("'"$ln1"'"|""'"$ln2"'")$"?j--:i]
  next
}1' infile

This is strange,
what error/wrong behavior you got?

This is on my system:

% print -l {1..10}>infile
% cat infile 
1
2
3
4
5
6
7
8
9
10
% awk 'NR == FNR {
quote>   if (NR ~ "^("ln1"|"ln2")$")
quote>     _[++c] = $0
quote>   next
quote>   }
quote> FNR ~ "^("ln1"|"ln2")$" { $0 = _[c--] } 
quote> 1' ln1=3 ln2=5 infile infile
1
2
5
4
3
6
7
8
9
10
% awk 'NR == ln1, NR == ln2 { 
quote>   _[++c] = $0
quote>   if (NR == ln2 && j = c) 
quote>     while (++i <= c)
quote>       print _[NR ~ "^("ln1"|"ln2")$"?j--:i]
quote>   next
quote> }1' ln1=3 ln2=5 infile
1
2
5
4
3
6
7
8
9
10

Basically, the output would just remain the same with no lines being swapped...I looked around and that "-v" command for awk, is only in certain versions of awk, so I might not be able to use it in the version I'm using.

Could you post the output of the following commands (copy/paste from your terminal):

printf "%d\n" 1 2 3 |
awk 'NR == ln1, NR == ln2 {
  _[++c] = $0
  if (NR == ln2 && j = c)
    while (++i <= c)
      print _[NR ~ "^("ln1"|"ln2")$"?j--:i]
  next
}1' ln1=1 ln2=3

Or if your shell supports process substitution:

% awk 'NR == FNR {
  if (NR ~ "^("ln1"|"ln2")$")
    _[++c] = $0
  next
  }
FNR ~ "^("ln1"|"ln2")$" { $0 = _[c--] }
1' ln1=1 ln2=3 <(printf "%d\n" 1 2 3) <(printf "%d\n" 1 2 3)
3
2
1

I get:
3
2
1

I guess maybe i just copied the code of the two initial examples incorrectly? I'll play around with it some more and see if i can get it working.

OK,
thank you for posting the result.

Hi, I'm a newbie in unix script. Currently i'm using script for my daily job. Can anyone here explain in detail above script? I mean the syntax and command line by line. I do understand the script swap the 3rd line with line 10. If i want to swap line no 2 with line no 3, i just need to change variable line=2 and line1=3 right?

TQ.

It should work, to learn this stuff is to read about it and find it out yourself, so try it out.

The idea of the script, to replace the 3th line with the 9th, is:

  1. print all lines untill line 3-1 (2nd line)
  2. place the 4th line untill line 9-1 (8th line) before the 3th line in the variable s separated by a newline
  3. print the 9th line
  4. print the variable s
  5. print other lines after the 9th line
#!/bin/sh
 
line=3
line1=10

awk -v var="$line" -v var1="$line1" 'NR==var {
  s=$0
  for(i=var+1;i<var1;i++){
    getline;s=$0"\n"s
  }
  getline;print;print s
  next
}1' file

Explanation of the code:

awk -v var="$line" -v var1="$line1" 

Place the shellvariables in awk variables.

NR==var

If the linenumber is equal to 3 execute the commands between the curly brackets {}

s=$0

Place the current line in the variable s

  for(i=var+1;i<var1;i++){
    getline;s=$0"\n"s
  }

Loop through the lines 4 till 8 and place the lines before the 3th line separated by a newline.

  getline;print;print s
  next

Read the next line (9th line), print the line, print the variable s and read the next line

1

awk evaluates the 1 as true and the prints the entire line by default, an equivalent of {print}.

Regards