2

I am doing a research project that requires shell scripting, which I have almost no experience in, although I do have some programming experience. Here is the file in question:

export OMP_NUM_THREADS=12


read controls
#inlist directory
export MESA_INLIST="/home/nick/mesa-r11701/star/test_suite/rsp_Cepheid_grid/inlist"

sed -i -e 's/.*extra_star_job_inlist2_name.*/extra_star_job_inlist2_name = '"'"''$controls"""'"'/i' $MESA_INLIST
sed -i -e 's/.*extra_controls_inlist2_name.*/extra_controls_inlist2_name = '"'"''$controls"""'"'/i' $MESA_INLIST

I am borrowing this file to change the input of this second file /home/nick/mesa-r11701/star/test_suite/rsp_Cepheid_grid/inlist. Specifically the extra_controls_inlist2_name and the extra_star_job_inlist2_name variables (what is after the right side of the equal sign).

! this is the master inlist that MESA reads when it starts.

! This file tells MESA to go look elsewhere for its configuration
! info. This makes changing between different inlists easier, by
! allowing you to easily change the name of the file that gets read.

&star_job

      read_extra_star_job_inlist1 = .true.
      extra_star_job_inlist1_name = 'inlist_0all'

      read_extra_star_job_inlist2 = .true.
extra_star_job_inlist2_name = 'inlist_3ms'

/ ! end of star_job namelist


&controls

      read_extra_controls_inlist1 = .true.
      extra_controls_inlist1_name = 'inlist_0all'

      read_extra_controls_inlist2 = .true.
extra_controls_inlist2_name = 'inlist_3ms'

/ ! end of controls namelist


&pgstar

/ ! end of pgstar namelist

I am already familiar with what the export and read commands do. I know that sed is a stream editor that is used for finding and replacing text, which is what my goal with the second file is here. However, what do the -i -e options do here exactly? Why are there so many quotes around $controls$? What do the /.* and the /i of the sed command mean? I tried to do a preliminary search for these online, but I could not find an appropriate answer.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
thecep1
  • 31
  • 6

1 Answers1

3
  1. -e means "the next command line argument is a sed expression".
  2. -i means "do this edit in-place, i.e. modify the original file". This is option is non-standard and may cause confusion when moving between different sed implementations on different Unix systems. See e.g. How can I achieve portability with sed -i (in-place editing)?
  3. /.* is the start of the regular expression to match the text that is supposed to be substituted. It's actually /, the delimiter for the s/// (substitution) command in sed, and the start of the regular expression .*extra_star_job_inlist2_name.*. The .* by itself means "match any number of any character".

    The .* also occurs at the end of the expression .*extra_star_job_inlist2_name.* and the effect is that any line that contains the string extra_star_job_inlist2_name will be replaced entirly (i.e. the substitution would not just affect the string extra_star_job_inlist2_name on the line).

  4. The /i is, again, the / delimiter for the s/// command in sed followed by a flag for the s/// command. This flag is non-standard, only available in the GNU flavor of sed. The flag makes the matching of the expression case-insensitive. See the GNU sed documentation about this.

  5. The weird quotes around $controls:

    • The sed expression is given in a single quoted string. Since it's a single quoted string, a variable would not be expanded inside it. To insert the value of a variable, the single quoted string must first end. That's the first '.
    • Then, apparently, the author of the code wants to insert a literal single quote. To do that, they quote it: "'". They could also have used \' here.
    • Then follows, for whatever reason, a single quoted empty string, ''
    • Then comes the value of $controls (unquoted).
    • Another empty double quoted string, "".
    • Another quoted single quote, "'".
    • And then the sed expression continues as a single quoted string.

There are a few issues with the quotes around $controls:

  1. There are two empty strings.
  2. The expansion of $controls is unquoted.

A better variation that avoids these issues is

sed -i -e "s/.*extra_star_job_inlist2_name.*/extra_star_job_inlist2_name = '$controls'/i" "$MESA_INLIST"

i.e. simply use a double quoted string for the whole sed expression (and also properly quote $MESA_INLIST, which, by the way, does not need to be exported with export). This would work as long as the sed expression itself does not contain backslashes or $ characters (these would need to be escaped as \\ and \$).

Or, to shorten it down a bit, use a capture group to capture the string that you want to repeat in the replacement text,

sed -i -e "s/.*\(extra_star_job_inlist2_name\).*/\1 = '$controls'/i" "$MESA_INLIST"

(and similarly for the other sed command.)

To still use single quotes fore the sed expression, you could do

sed -i -e 's/.*\(extra_star_job_inlist2_name\).*/\1 = '"'$controls'"'/i' "$MESA_INLIST"

Another optimization would be to combine both calls to sed into a single call:

sed -i -e "s/.*\(extra_star_job_inlist2_name\).*/\1 = '$controls'/i" \
       -e "s/.*\(extra_controls_inlist2_name\).*/\1 = '$controls'/i" "$MESA_INLIST"
Kusalananda
  • 333,661
  • ♦ Thank you for this lucid explanation! Please see my upcoming response below to see if I understand correctly. – thecep1 May 17 '20 at 17:23