How to replace a string on the 5th line of multiple text files?
I want to replace the 5th line of multiple text files (file1.txt,file2.txt,file3.txt,file4.txt) with the string " Good Morning " using a single terminal command.
All the text files are located on my ~/Desktop
.
Note: My desktop consists of 6 .txt files.I want to apply the change to above mentioned 4 text files only.
Here are a few approaches. I am using brace expansion (file{1..4}.txt
) which means file1.txt file2.txt file3.txt file4.txt
-
Perl
perl -i -pe 's/.*/ Good Morning / if $.==5' file{1..4}.txt
Explanation:
-
-i
: causesperl
to edit the files in place, changing the original file.If
-i
is followed with a file extension suffix, then a backup is created for every file that is modified. Ex:-i.bak
creates afile1.txt.bak
iffile1.txt
is modified during the execution. -p
: means read the input file line by line, apply the script and print it.-e
: allows you to pass a script from the command line.s/.*/ Good Morning /
: That will replace the text in the current line (.*
) with Good Morning.$.
is a special Perl variable that holds the current line number of the input file. So,s/foo/bar/ if $.==5
, means replacefoo
withbar
only on the 5th line.
-
-
sed
sed -i '5s/.*/ Good Morning /' file{1..4}.txt
Explanation:
-
-i
: Like forperl
, edit file in place.
By default,
sed
prints each line of the input file. The5s/pattern/replacement/
means substitute pattern with replacement on the 5th line. -
-
Awk
for f in file{1..4}.txt; do awk 'NR==5{$0=" Good Morning "}1;' "$f" > foobar && mv foobar "$f"; done
Explanation:
awk
has no equivalent to the-i
option¹ which means that we need to create a temporary file (foobar
) which is then renamed to overwrite the original. The bash loopfor f in file{1..4}.txt; do ... ; done
simply goes through each offile{1..4}.txt
, saving the current file name as$f
. Inawk
,NR
is the current line number and$0
is the content of the current line. So, the script will replace the line ($0
) with " Good Morning " only on the 5th line.1;
isawk
for "print the line".¹Newer versions do as devnull showed in his answer.
-
coreutils
for f in file{1..4}.txt; do (head -4 "$f"; echo " Good Morning "; tail -n +6 "$f") > foobar && mv foobar "$f"; done
Explanation:
The loop is explained in the previous section.
head -4
: print the first 4 linesecho " Good Morning "
: print " Good Morning "tail -n +6
: print everything from the 6th line to the end of the file
The parentheses
( )
around those three commands allow you to capture the output of all three (so, 1st 4 lines, then " Good morning ", then the rest of the lines) and redirect them to a file.
You could use sed
:
sed '5s/^/Good morning /' file
would append Good morning
on the fifth line of a file.
If you want to replace the contents on line 5 instead, say:
sed '5s/.*/Good morning/' file
If you wanted to save the changes to the file in-place, use the -i
option:
sed -i '5s/.*/Good morning/' file
sed
can handle more than one file at a time. You can just add more filenames onto the end of the command. You can also use bash expansions to match particular files:
# manually specified
sed -i '5s/.*/Good morning/' file1.txt file2.txt file3.txt file4.txt
# wildcard: all files on the desktop
sed -i '5s/.*/Good morning/' ~/Desktop/*
# brace expansion: file1.txt, file2.txt, file3.txt, file4.txt
sed -i '5s/.*/Good morning/' file{1..4}.txt
You can read more about brace expansions here.
GNU awk versions 4.1.0 and higher come with an extension that enable in-place editing. So you could say:
gawk -i inplace 'NR==5{$0="Good morning"}7' file
to replace line #5 in the file with Good morning
!
I didn't see python solution so here it is:
import sys
import os
def open_and_replace(filename):
with open(filename) as read_file:
temp = open("/tmp/temp.txt","w")
for index,line in enumerate(read_file,1):
if index == 5:
temp.write("NEW STRING\n")
else:
temp.write(line.strip() + "\n")
temp.close()
os.rename("/tmp/temp.txt",filename)
for file_name in sys.argv[1:]:
open_and_replace(file_name)
Basic idea is that for each file provided on command-line as argument, we write out a temporary file and enumerate each line in the original file. If index of the line is 5, we write out line that is different. The rest is just replacing old file with temp file Demo:
$> ls
file1.txt file2.txt file3.txt
$> cat file1.txt
line 1
line 2
line 3
line 4
GOOD MORNING
line 6
$> python ~/replace_5th_line.py file1.txt file2.txt file3.txt
$> cat file1.txt
line 1
line 2
line 3
line 4
NEW STRING
line 6
$> cat file2.txt
line 1
line 2
line 3
line 4
NEW STRING
line 6
The same can be achieved with list comprehension. Below is a one-liner of the same script:
cat /etc/passwd | python -c 'import sys; print "\n".join(["CUSTOM" if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])'
or without cat
python -c 'import sys; print "\n".join(["CUSTOM" if index == 5 else line.strip() for index,line in enumerate(sys.stdin,1)])' < /etc/passwd
What is left there is to simply redirect output of edited contents into another file with > output.txt