To update a system configuration file on a Linux server I was in need to add a rule (line of text) before a given other rule (and, if possible, before the comments of that other rule).
With the following input file:
# Foo
# Bar
# Comment about rule on the next line
RULE_A
# Comment about rule on the next line
# Continuation of comment
RULE_B
I want to get the following output:
# Foo
# Bar
# Comment about rule on the next line
RULE_A
# ADDED COMMENT
# ADDED COMMENT CONTINUATION
ADDED_RULE
# Comment about rule on the next line
# Continuation of comment
RULE_B
I ended up with the following combination of :
sed
: convert my multi-line text to add in a single line with\n
.tac
: to reverse the file.awk
: to work on the file.- A temporary file that will replace the original file (because I don't have "in-place" option on
awk
)
CONF_FILEPATH="sample.conf"
# Create sample work file:
cat > "${CONF_FILEPATH}" <<EOT
# Foo
# Bar
# Comment about rule on the next line
RULE_1
# Comment about rule on the next line
# Continuation of comment
RULE_2
RULE_3_WITHOUT_COMMENT
RULE_4_WITHOUT_COMMENT
RULE_5_WITHOUT_COMMENT
# Comment about rule on the next line
RULE_6
# Comment about rule on the next line
# Continuation of comment
RULE_7
EOT
# Text (of new rule) to add:
TEXT_TO_ADD="# ADDED COMMENT
# ADDED COMMENT CONTINUATION
ADDED_RULE
"
# The rule before which we want to add our text:
BEFORE_RULE="RULE_7"
# Temporary file:
TMP_FILEPATH="$(mktemp)"
# Convert newlines to \n:
TEXT_TO_ADD_FOR_AWK="$(echo ${TEXT_TO_ADD} | tac | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g')"
# Process
awk 'BEGIN {
ADD_TO_LINE="";
}
{
if ($0 ~ "^'${BEFORE_RULE}'") {
# DEBUG: Got the "deny all" line
ADD_TO_LINE=NR+1 ;
print $0;
} else {
if (ADD_TO_LINE==NR) {
# DEBUG: Current line is the candidate
if ($0 ~ "#") {
ADD_TO_LINE=NR+1;
# DEBUG: Its a comment, wont add here, taking note to try on the next line
print $0;
} else {
# DEBUG: Not a comment: this is the place!
print "'${TEXT_TO_ADD_FOR_AWK}'";
ADD_TO_LINE="";
print $0;
}
} else {
print $0;
}
}
}' <(tac "${CONF_FILEPATH}") \
| tac > "${TMP_FILEPATH}"
# Overwrite:
cat "${TMP_FILEPATH}" > "${CONF_FILEPATH}"
# Cleaning up:
rm "${TMP_FILEPATH}"
I then get (look just before RULE_7
):
# Foo
# Bar
# Comment about rule on the next line
RULE_1
# Comment about rule on the next line
# Continuation of comment
RULE_2
RULE_3_WITHOUT_COMMENT
RULE_4_WITHOUT_COMMENT
RULE_5_WITHOUT_COMMENT
# Comment about rule on the next line
RULE_6
# ADDED COMMENT
# ADDED COMMENT CONTINUATION
ADDED_RULE
# Comment about rule on the next line
# Continuation of comment
RULE_7
Which is OK, but I'm sure there is a cleaner/simpler way of doing that with awk
.
Context: I am editing the /etc/security/access.conf
to add an allow rule before the deny all rule.