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.

for file in f1 f2; do
  sed -i '8s/^.*$/foo/' "$file"
  • 3
    Why not just sed -i '8s/^.*$/foo/' f1 f2?
    – l0b0
    Jun 21 '13 at 21:51
  • @l0b0 Have you tried that? Jun 21 '13 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 '13 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 '13 at 22:10
  • 2
    Try with -s: "consider files as separate rather than as a single continuous long stream."
    – l0b0
    Jun 22 '13 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.