ChatGPT解决这个技术问题 Extra ChatGPT

Insert a line at specific line number with sed or awk

I have a script file which I need to modify with another script to insert a text at the 8th line.

String to insert: Project_Name=sowstest, into a file called start.

I tried to use awk and sed, but my command is getting garbled.


V
Vishrant
sed -i '8i This is Line 8' FILE

inserts at line 8

This is Line 8

into file FILE

-i does the modification directly to file FILE, no output to stdout, as mentioned in the comments by glenn jackman.


Yes, the -i switch is specific for GNU-sed.
No. -i means "modify the specified file in place". Insert vs. append is achieved with '8isomething' vs. '8asomething', independent from the -i-switch.
mac users: with homebrew, brew install gnu-sed and then use this with gsed
This is super useful! Is there anyway for me to insert spaces at the beginning of the line? I noticed sed is not paying attention to initial whitespace...
@elju: Yes, mask it with a backslash: sed '8i\ 8 This is Line 8' FILE.
M
Mateusz Piotrowski

An ed answer

ed file << END
8i
Project_Name=sowstest
.
w
q
END

. on its own line ends input mode; w writes; q quits. GNU ed has a wq command to save and quit, but old ed's don't.

Further reading: https://gnu.org/software/ed/manual/ed_manual.html


M
Mateusz Piotrowski

OS X / macOS / FreeBSD sed

The -i flag works differently on macOS sed than in GNU sed.

Here's the way to use it on macOS / OS X:

sed -i '' '8i\
8 This is Line 8' FILE

See man 1 sed for more info.


Is it possible to do this with a variable instead of a string literal inline?
@AndyRay Use double quotes instead. The shell variable will expand then.
g
glenn jackman

the awk answer

awk -v n=8 -v s="Project_Name=sowstest" 'NR == n {print s} {print}' file > file.new

@glenn jackman I need to enter #define SERVER@"http://10.35.42.54/ms0.8" to a particular line. How can I achieve this?
I think Nevin just needs to escape the quotes in his string with backslashes
@waLLe, start with the awk info page which has a nice description of how awk works. Here, I have 2 "condition {action}" pairs, the 2nd has no condition which means the action is performed for every record. After you finish reading and you still have questions, let me know.
@glennjackman almost there.. just didn't get this: file > file.new
"file" represents the name of the file that awk is working on. > is the shell redirection symbol so that awk's output is stored in the file named "file.new".
L
Lri

POSIX sed (and for example OS X's sed, the sed below) require i to be followed by a backslash and a newline. Also at least OS X's sed does not include a newline after the inserted text:

$ seq 3|gsed '2i1.5'
1
1.5
2
3
$ seq 3|sed '2i1.5'
sed: 1: "2i1.5": command i expects \ followed by text
$ seq 3|sed $'2i\\\n1.5'
1
1.52
3
$ seq 3|sed $'2i\\\n1.5\n'
1
1.5
2
3

To replace a line, you can use the c (change) or s (substitute) commands with a numeric address:

$ seq 3|sed $'2c\\\n1.5\n'
1
1.5
3
$ seq 3|gsed '2c1.5'
1
1.5
3
$ seq 3|sed '2s/.*/1.5/'
1
1.5
3

Alternatives using awk:

$ seq 3|awk 'NR==2{print 1.5}1'
1
1.5
2
3
$ seq 3|awk '{print NR==2?1.5:$0}'
1
1.5
3

awk interprets backslashes in variables passed with -v but not in variables passed using ENVIRON:

$ seq 3|awk -v v='a\ba' '{print NR==2?v:$0}'
1
a
3
$ seq 3|v='a\ba' awk '{print NR==2?ENVIRON["v"]:$0}'
1
a\ba
3

Both ENVIRON and -v are defined by POSIX.


j
jno

sed -e '8iProject_Name=sowstest' -i start using GNU sed

Sample run:

[root@node23 ~]# for ((i=1; i<=10; i++)); do echo "Line #$i"; done > a_file
[root@node23 ~]# cat a_file
Line #1
Line #2
Line #3
Line #4
Line #5
Line #6
Line #7
Line #8
Line #9
Line #10
[root@node23 ~]# sed -e '3ixxx inserted line xxx' -i a_file 
[root@node23 ~]# cat -An a_file 
     1  Line #1$
     2  Line #2$
     3  xxx inserted line xxx$
     4  Line #3$
     5  Line #4$
     6  Line #5$
     7  Line #6$
     8  Line #7$
     9  Line #8$
    10  Line #9$
    11  Line #10$
[root@node23 ~]# 
[root@node23 ~]# sed -e '5ixxx (inserted) "line" xxx' -i a_file
[root@node23 ~]# cat -n a_file 
     1  Line #1
     2  Line #2
     3  xxx inserted line xxx
     4  Line #3
     5  xxx (inserted) "line" xxx
     6  Line #4
     7  Line #5
     8  Line #6
     9  Line #7
    10  Line #8
    11  Line #9
    12  Line #10
[root@node23 ~]# 

Where is the trailing $ coming from on line 3 after the insertion?
It's from -A flag to cat :)
what if the string you want to insert contains quote marks, parentheses etc?
See update to the text above. That means, you will have to escape those chars as appropriate.
C
Chris Koknat

Perl solutions:

quick and dirty:

perl -lpe 'print "Project_Name=sowstest" if $. == 8' file

-l strips newlines and adds them back in, eliminating the need for "\n"

-p loops over the input file, printing every line

-e executes the code in single quotes

$. is the line number

equivalent to @glenn's awk solution, using named arguments:

perl -slpe 'print $s if $. == $n' -- -n=8 -s="Project_Name=sowstest" file

-s enables a rudimentary argument parser

-- prevents -n and -s from being parsed by the standard perl argument parser

positional command arguments:

perl -lpe 'BEGIN{$n=shift; $s=shift}; print $s if $. == $n' 8 "Project_Name=sowstest" file

environment variables:

setenv n 8 ; setenv s "Project_Name=sowstest"
echo $n ; echo $s
perl -slpe 'print $ENV{s} if $. == $ENV{n}' file

ENV is the hash which contains all environment variables

Getopt to parse arguments into hash %o:

perl -MGetopt::Std -lpe 'BEGIN{getopt("ns",\%o)}; print $o{s} if $. == $o{n}' -- -n 8 -s "Project_Name=sowstest" file

Getopt::Long and longer option names

perl -MGetopt::Long -lpe 'BEGIN{GetOptions(\%o,"line=i","string=s")}; print $o{string} if $. == $o{line}' -- --line 8 --string "Project_Name=sowstest" file

Getopt is the recommended standard-library solution. This may be overkill for one-line perl scripts, but it can be done


Kudos to you Chris for taking the time to explain all that and lay it out so nicely, but wow this is why I don't like perl.
N
Nasri Najib

For those who are on SunOS which is non-GNU, the following code will help:

sed '1i\^J
line to add' test.dat > tmp.dat

^J is inserted with ^V+^J

Add the newline after '1i.

\ MUST be the last character of the line.

The second part of the command must be in a second line.


b
buddemat

it is working fine in linux to add in 2 lines.

sed '2s/$/ myalias/' file

u
umläute
sed -i "" -e $'4 a\\\n''Project_Name=sowstest' start

This line works fine in macOS


I
IAng

Thank you umläute

sed -i "" -e $'4 a\\\n''Project_Name=sowstest' filename

the following was usefull on macOS to be able to add a new line after the 4

In order to loop i created an array of folders, ti iterate on them in mac zsh

for foldercc in $foldernames; 

sed -i "" -e $'4 a\\\n''Project_Name=sowstest' $foldercc/filenames;