Strang84
Strang84

Reputation: 43

How to search a string of text and insert it as a variable using sed?

I need to update potentially hundreds of configuration files for a program by adding new lines to text files. I will be adding additional properties, such as background color, to these files and want to automate the process with bash. All of these properties are contained in ".m" files. Its essentially updating the properties of widgets on GUIs. Each object's properties in a gui is labeled with an object type followed by a name. One example of an object is called a Form.

The problem is, each name that follows the object is different so I need to add the line based off of the name of the other properties in each section of the .m file. For example, one file has two form objects. The section for one is called "*FO_mm" while the second object's section is named "*FO_test_area". After the name is an extension for what property is specified, such as "*FO_mm.class:". While the properties each object has tends to vary, I found that all objects share a property called ".parent" so I am using that as a search reference. I want to use the sed command to add a line after the .parent line with the new property, in this case background color. So the idea is to search for a string that starts with "*FO_" and ends with ".parent", with everything inbetween being something different for each section. I want to use a loop to capture the string preceding ".parent" as a variable and attach it to the beginning of the new property line so it matches the current section. Here is my current script:

//The top level directory
script_dir="/project/guis/"
//The extension to look for
file_ext="*.m"
fileList=$(find $script_dir -type f -name "$file_ext")
declare -a file_list
readarray -t file_list < <(printf '%s\n' "$fileList")
cd $script_dir
//Loop through each m file
for m_file in ${file_list[@]}; do
        var1=($(grep '*FO_.*.parent:' $m_file))
        declare -a var_list
        readarray -t var_list < <(printf '%s\n' "$var1")
        for i in ${var_list[@]}; do
                echo $i
                sed -i "/^*FO_.*.parent:.*/a\$i.background: #2b3856 " $m_file
        done
done

When I run it, the script adds the line "$i.background: #2b3856" below the .parent line. And the "echo $i" line returns "*FO_mm.parent: FO_mm". So there are several problems.

  1. The value of the variable is not being substituted into the sed statement.
  2. As the echo states, only the first section "*FO_mm" is being saved as a variable, which means the second section "*FO_test_area" is not being implemented.
  3. I only want the object name to be stored and placed into the new line. So the result should give me for the first section "*FO_mm.background: #2b3856" with everything from .background on being tacked on by the last part of the sed statement. Since I am still fairly new to bash and especially sed, I have no idea how to strip the variable down to just the object name.

Here is an example of what a single object section looks like prior to running the script:

*FO_test_area.class: Form
*FO_test_area.static: true
*FO_test_area.parent: FO_mm
*FO_test_area.resizePolicy: "resize_none"

And here is what this section looks like after running the WIP script:

*FO_test_area.class: Form
*FO_test_area.static: true
*FO_test_area.parent: FO_mm
$i.background: #33b342
*FO_test_area.resizePolicy: "resize_none"

Its a lot to describe, but I've hit a wall and I would really appreciate any help you can provide.

Upvotes: 2

Views: 122

Answers (1)

Jetchisel
Jetchisel

Reputation: 7831

If ed is available/acceptable.

The script named script.ed (name it whatever you like).

g/^\*FO_.*\.parent:.*/t.\
s/^\(\*\).*parent: */\1/
s/$/.background: #33b342/
%p
Q

The g/^\*FO_.*\.parent:.*/ will match every line that starts with *FO_ and with .parent: somewhere after it. It will match either *FO_test_area.parent and *FO_mm.class.parent. You gonna have to be specific about the regex to match a specific *FO_.*\.parent: pattern to be able to do a specific search & replace/insert


Here is a specific script for the *FO_test_area.parent:

g/^\*FO_test_area\.parent:.*/t.\
s/^\(\*\).*parent: */\1/
s/$/.background: #33b342/
%p
Q

Modify the script above and add another pattern before the line where %p is at, do the rest of the substitution after that.


Your sample file/data.

*FO_test_area.class: Form
*FO_test_area.static: true
*FO_test_area.parent: FO_mm
*FO_test_area.resizePolicy: "resize_none"

Running the script against your data/file. (file ending with a .m)

ed -s file.m < script.ed

Output

*FO_test_area.class: Form
*FO_test_area.static: true
*FO_test_area.parent: FO_mm
*FO_mm.background: #33b342
*FO_test_area.resizePolicy: "resize_none"

If you're satisfied with the output, next thing is to do the loop.

Doing some adjustment to your script. Instead of a nested for loop, the script is using a while + read loop and Process Substitution. See How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?

#!/usr/bin/env bash

script_dir="/project/guis/"
file_ext="*.m"

while IFS= read -r m_file; do
  if grep -q '^\*FO_.*.parent:' "$m_file"; then
    ed -s "$m_file" < script.ed
  fi
done < <(find "$script_dir" -type f -name "$file_ext")

  • The script.ed is inside the current directory where your script is at. It can be anywhere just need to give it the correct absolute path, e.g. /path/to/script.ed

  • If in-place editing is needed change the Q to w inside the ed script.

  • Remove the line where %p is at if the output is not needed to stdout.


See:

Also your local man pages.

man 1p ed

Upvotes: 1

Related Questions