Reputation: 43
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.
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
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