Suppose that I have ten bash shell scripts: script1.sh, script2.sh, ..., script10.sh. Initially, all ten files are identical.

Now, I would like to make two changes in each script:

  1. In each file, I would like to change a particular line (say line 8) -- that is, deleting whatever is on line 8 and replacing it with a "constant" string that I specify, such as "This is line 8." This is similar to this question, but there they wanted to replace "AAA" with "BBB", whereas I would like to replace line 8 (whatever it is) with "This is line 8.".

  2. In each file, I would like to change another particular line (say line 21) and replace it with a "variable" string that I specify. For example, in script1.sh I want to change line 21 to "XYZ"; in script2.sh I want to change line 21 to "PQR"; and in script3.sh I want to change line 21 to "ABC". Essentially this is just many calls to the function in (1) above -- except that I would be making the change in one individual file rather than in all files, and that I am specifying ten different strings rather than just one. So to obtain (2) here, perhaps I would just call (1) ten different times with different parameters.

I am interested in solutions that use commonly available Linux programs like bash, vi, awk, gawk, etc.

3 Answers 3

for file in f1 f2; do
  sed -i '8s/^.*$/foo/' "$file"
  • 3
    Why not just sed -i '8s/^.*$/foo/' f1 f2?
    – l0b0
    Jun 21, 2013 at 21:51
  • @l0b0 Have you tried that? Jun 21, 2013 at 21:59
  • 2
    Worked perfectly: echo test1 > f1; echo test2 > f2; sed -i '1s/^.*$/foo/' f1 f2. cat f1 f2 shows that both were changed to foo.
    – l0b0
    Jun 21, 2013 at 22:05
  • @l0b0 That's funny. I tried that first but without -i. And in that case just one line was changed. Doesn't make sense to me. A bug (GNU sed-Version 4.2.1)? Jun 21, 2013 at 22:10
  • 2
    Try with -s: "consider files as separate rather than as a single continuous long stream."
    – l0b0
    Jun 22, 2013 at 8:28

Using awk

for file in script1.sh script2.sh script3.sh ... script10.sh; do
    awk '
        BEGIN {
            line8 = "This is line 8"
            line21["script1.sh"] = "XYZ"
            line21["script2.sh"] = "PQR"
            line21["script3.sh"] = "ABC"
            # ... 
        FNR == 8 { print line8; next }
        FNR == 21 && FILENAME in line21 { print line21[FILENAME]; next }
    '  "$file" > "$temp" && mv "$temp" "$file"

or, using bash and sed

# bash variables to store new values. requires bash v4 for the associative array
line8="This is line 8"
declare -A line21=(
    # ...
# then use sed
for file in script1.sh script2.sh script3.sh ... script10.sh; do
    sed -i "8s/.*/$line8/; 21s/.*/${line21[$file]}/" "$file"

With the sed solution, you have to be careful that the new lines do not contain "/" characters, as that will break the s/// command. Although you can use different delimiter characters, such as s|pattern|replacement| (in which case the same warning applies, recursively)

ed works too:

for file in script1.sh script2.sh script3.sh ... script10.sh; do
    ed "$file" <<ED_COMMANDS

I will answer both questions but, as a general rule, please don't combine questions like this, split them into separate posts instead.

Question 1

  1. Perl:

    for f in *.sh; do 
      perl -i -lne '$.==8 ? print "This is line 8" : print;' "$f"; 
  2. awk and variants:

    for f in *.sh; do 
      awk '{if(NR==8){print "This is line 8"}else{print}}' "$f" > fifo && 
      mv fifo "$f";

Question 2

I would first create a file with the replacements I want to make. For example:

script1.sh  foo1
script2.sh  foo2
script3.sh  foo3
script4.sh  foo4

Then read that file to perform the substitutions:

while read f s; do 
 export s;
 perl -i -lne '$.==8 ? print "$ENV{s}" : print;' "$f";
done < sublist.txt

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .