1

A script that creates a file for weather precipitation is,

#!/bin/ksh

x=10
b=snow
c=rain
d=snow
e=hail
echo "$x,$b,$c,$d,$e" > weatherfile

exit 0

given

3,8s/snow/dry/g

For the search and replace, what is the best way to implement this in a script?

From the command perspective would sed be necessary?

My thoughts are that the following should work:

sed 3,8s/snow/dry/g weatherfile

Though what if sed could not be used. Would I need to open and close the file and some how run

:3,8s/snow/dry/g while the file is open? How can this be implemented?

2

You can use vim commands directly with a here-document (that is especially useful for irregular replacements):

#!/bin/ksh
vim weatherfile <<EOF
:3,8s/snow/dry/g
:wq
EOF
0

You can do it entirely in the shell with a script like this:

#!/bin/bash
line_num=0
while IFS= read -r line
do
        : $((line_num++))
        if [[ line_num -ge 3  &&  line_num -le 8 ]]
        then
                printf "%s\n" "${line/snow/dry}"
        else
                printf "%s\n" "$line"
        fi
done

(I've tested this in bash, and Andrey reports that it works in ksh, but it won't work in all POSIX-compliant versions of sh.)  It reads lines from the standard input.  It counts lines (with line_num=0 and : $((line_num++)) because you want to specify a line number range; if that requirement is removed, the statements referencing line_num can be deleted.  The conditional test

        if [[ line_num -ge 3  &&  line_num -le 8 ]]

means “IF (line_num ≥ 3)  AND  (line_num ≤ 8)”, corresponding to the range 3,8, i.e., 3 through 8.  (Which is a point of confusion for Unix newbies, who expect 3,8 to mean “3 and also 8”, and expect “3 through 8” to be represented as 3-8 — i.e., the convention used by print dialog boxes in Windows for printing a subset of the pages of a document.)

On the lines that are in the range, it uses the

        ${parameter/pattern/string)

form of parameter expansion to replace "snow" with "dry" before writing the line out to the standard output.  Lines outside the range are written unmodified.

This will change the first occurrence (if any) of "snow" on each line within the range.  To replace all occurrences, use ${line//snow/dry} (insert an extra / before "snow").

I'm not clear on the big picture of what you want.

  • You could incorporate the above code into your existing script.
  • If you modified your script (let's call it weather.sh) to write to stdout rather than the file, then you could say

    ./weather.sh | ./do_substitute.sh > weatherfile
    
  • If you want to create weatherfile the same way you are doing it now, but then change it afterwards, you could do

    ./do_substitute.sh < weatherfile > weatherfile.tmp
    mv weatherfile.tmp weatherfile
    
  • The script works in ksh (93u+ 2012-02-29). – Andrey May 8 '15 at 16:31
  • @Andrey: Thanks for the information; I've edited it into my answer. – G-Man May 8 '15 at 19:13
  • Makes sense, I seem to understand the usage of the loop. So, for each line the snow is left unmodified unless it meets the conditions you placed [[ line_num -ge 3 && line_num -le 8 ]] – Juan Davila May 8 '15 at 21:59
  • Right; “(line_num ≥ 3) AND (line_num ≤ 8)” corresponds to the range 3,8, i.e., 3 through 8.  (Which is a point of confusion for Unix newbies, who expect 3,8 to mean “3 and also 8”, and expect “3 through 8” to be represented as 3-8 — i.e., the convention used by print dialog boxes in Windows for printing a subset of the pages of a document.) – G-Man May 8 '15 at 22:57

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.