Enable or disable sections of a model according to user input I am using sed command to comment in or out sections in my model according to user input (commenting is done by prefixing the line with !).  Once this is done, I rebuild my model to get a new executable. An example of a model file (named as scm_code_ex.txt in the script) is:

code
code
code-auto
code-auto
code-auto
code
code
code
code
code-agg
code-agg
code-agg
code
code
code
code
code
code
code
code


My script asks the user which sections of the code to comment out (off means comment out):

#!/bin/bash
# This script will comment in or out sections in the code text file
echo "- Aggregation (on) or (off) (type in ans)?"
read agg

echo "- Autoconversion (on) or (off) (type in ans)?"
read auto


#Revert to original with everything ON.

#On autoconversion
    sed -e'3s/\!//' scm_code_ex.txt &gt;scm_code_ex.tmp
    sed -e'4s/\!//' scm_code_ex.tmp &gt;scm_code_ex.tmp2
    sed -e'5s/\!//' scm_code_ex.tmp2 &gt;scm_code_ex.tmp3

#On aggreagation
    sed -e '10s/\!//' scm_code_ex.tmp3 &gt; scm_code_ex.tmp4
    sed -e '11s/\!//' scm_code_ex.tmp4 &gt; scm_code_ex.tmp5
    sed -e '12s/\!//' scm_code_ex.tmp5 &gt; scm_code_ex.txt


#Remove unwanted files
rm *.tmp*


#Modifications according to conditions

if [ "$agg" == 'on' ] &amp;&amp; [ "$auto" == 'on' ]; then
#the conditional statemet above needs spacing in 
#all the right steps otherwise it will not work

echo ""
echo "agg on ... auto on "
echo "Rebuild model now"
echo ""

elif [ "$agg" == 'off' ] &amp;&amp; [ "$auto" == 'off' ]; then

#commenting out autoconversion section lines 3-5
    sed -e "3s/^/!/g" scm_code_ex.txt &gt; scm_code_ex.tmp
    sed -e "4s/^/!/" scm_code_ex.tmp &gt; scm_code_ex.tmp2
    sed -e "5s/^/!/" scm_code_ex.tmp2 &gt; scm_code_ex.tmp3

    #the ^ means go to the beginning of the line and add the character !

#commenting out aggregation section lines 10-12
    sed -e "10s/^/!/" scm_code_ex.tmp3 &gt; scm_code_ex.tmp4
    sed -e "11s/^/!/" scm_code_ex.tmp4 &gt; scm_code_ex.tmp5
    sed -e "12s/^/!/" scm_code_ex.tmp5 &gt; scm_code_ex.txt

#Remove unwanted files
rm *.tmp*

echo ""
echo "agg off ... auto off "
echo "Rebuild model now"
echo ""

elif [ "$agg" == 'on' ] &amp;&amp; [ "$auto" == 'off' ]; then

#commenting out autoconversion section lines 2-5
    sed -e "3s/^/!/g" scm_code_ex.txt &gt; scm_code_ex.tmp
    sed -e "4s/^/!/" scm_code_ex.tmp &gt; scm_code_ex.tmp2
    sed -e "5s/^/!/" scm_code_ex.tmp2 &gt; scm_code_ex.txt

#Remove unwanted files
rm *.tmp*

echo ""
echo "agg on ... auto off "
echo "Rebuild model now"
echo ""

elif [ "$agg" == 'off' ] &amp;&amp; [ "$auto" == 'on' ]; then

#commenting out aggregation section lines 10-12

    sed -e "10s/^/!/" scm_code_ex.txt &gt; scm_code_ex.tmp4
    sed -e "11s/^/!/" scm_code_ex.tmp4 &gt; scm_code_ex.tmp5
    sed -e "12s/^/!/" scm_code_ex.tmp5 &gt; scm_code_ex.txt

#Remove unwanted files
rm *.tmp*

echo ""
echo "agg off ... auto on"
echo "Rebuild model now"
echo ""
fi  


I'm very new to programming so I'm wondering how to make this better. For example, can sed be applied to a range of lines in one command? (I could not find something similar). To comment out lines 3-5, I tried sed "3,5{s/^/!/}" scm_code_ex.txt but that didn't work.
Since we're using Bash, we can combine the echo and read by using -p (prompt) argument to read like this:

read -p 'Enable aggregation [on/off] ' agg


I slightly adapted the form of the prompt to be more like common conventions from other interactive programs.

It's a good idea to validate the user input.  If On is entered instead of on, then the user might be surprised to find it's equivalent to off!

unset agg
until [ "$agg" = on ] || [ "$agg" = off ]
do
    read -p 'Enable aggregation [ON/off] ' agg
    agg="${agg:-on}"
done


This also adds a default; that may or may not be appropriate here.



The multiple sed invocations could be combined into one:

sed -e "3s/^/!/" -e "4s/^/!/" -e "5s/^/!/" scm_code_ex.txt 


Since we want to perform the same operation on a range of lines, we can use an address range (3,5), and we can write the output file in place of the original (-i):

sed -i -e '3,5s/^/!/' scm_code_ex.txt


(The form you tried didn't work, because you need to separate the { from the enclosed command inside the group - but since there's only a single command, we don't need a group.)

We probably don't want to be doing the in-place editing: it's better to have a pristine (read-only) source file, and always generate the output file starting from there (we can even prefix it with a "DO NOT EDIT" comment).  This makes it more amenable to generation by Make, as well as saving your bacon if you ever make a mistake in the script.



Instead of duplicating the commands for aggregation, one thing we can do is build the appropriate sed command using the options, then run it once.  I would do this using Bash array variables:

replacements=()
[ "$auto" = on ] || replacements+=( -e '3,5s/^/!/' )
[ "$agg" = on ] || replacements+=( -e '10,12s/^/!/' )

sed -e "${replacements[@]}" scm_code_all.txt &gt;scm_code.txt


Now we no longer need all those if/elif/fi blocks, and we no longer double the code size each time we add a new option.



The remaining fragile element in these scripts is the line-number addressing; this makes it hard to see which lines will be enabled or disabled, and it's hard to keep track when we add or remove lines from the source file.  We can add some markers to this file (I'm assuming that ! begins a comment in that language):

code
code

!##BEGIN AUTO
code-auto
code-auto
code-auto
!##END AUTO

code
code

!##BEGIN AGG
code-agg
code-agg
!##END AGG

code
code


We can then use those lines for addressing:

[ "$auto" = on ] || replacements+=( -e '/^!##BEGIN AUTO/,/^!##END AUTO/s/^/!/' )
[ "$agg" = on ] || replacements+=( -e '/^!##BEGIN AGG/,/^!##END AGG/s/^/!/' )


We can reduce the amount of repetition if instead of using variables for auto and agg, we keep a list of features, and loop through them:

features=()
for i in auto agg
do
    while read -p "Enable '$i' feature? [YES/no] " f
    do case "${f,,}" in
           y|yes|on|true|'') features+=($i); break;;
           no|off|false) break;;
       esac
    done
done

replacements=()
for i in ${features[*]}
do
    replacements+=( -e "/^!##BEGIN ${i^^}/,/^!##END ${i^^}/s/^/!/" )
done


And we can combine the two loops into one, thus:

replacements=()
for i in auto agg
do
    while read -p "Enable '$i' feature? [YES/no] " f
    do case "${f,,}" in
           y|yes|on|true|'') 
               replacements+=( -e "/^!##BEGIN ${i^^}/,/^!##END ${i^^}/s/^/!/" );
               break;;
           n|no|off|false)
               break;;
       esac
    done
done




With these changes, we no longer need to remove any intermediate temporary files, but I'll note that the wildcard used is rather broad and may catch the user's own temporaries.  Also, the temporaries are in the current working directory, not respecting $TMPDIR.  To fix both of those, I prefer to create a temporary directory with mktemp -d, and trap the EXIT signal to remove all temporaries:

tmpdir=$(mktemp -d)
trap 'rm -r "$tmpdir"' EXIT




I hope this answer gives some food for thought.  If you want a review of your code after considering the above, feel free to ask a new question with your amended code.