Reputation: 23
I have a requirement to move [mysqld] section in a mysql configuration file above another section named #SAFE#
in a mysql configuration file and after the section is moved there should be space between the sections,Please find the below code and requirement:
code used:
#!/bin/bash
for server in `cat /home/servers.txt'`
username=$1
password=$2
do
sshpass-p $PWD ssh -o stricthostchecking=no $USERNAME@$server "ed -s /home/my.cnf <<'EOF'
/^\[mysqld\]$/;/^\[.*/ -1m$
?\[mysqld\]?i
.
w
EOF"
done
Actual my.cnf:
[client]
a=2
b=7
[mysql]
d=6
e=7
[mysqld]
disable-log-bin = 1
skip-name-resolve = 1
performance-schema = 0
local-infile = 0
mysqlx = 0
open_files_limit = 200000
max_allowed_packet = 256M
sql_mode="NO_ENGINE_SUBSTITUTION"
#the below are password related of [mysqld]
validate password=1
innodb_dedicated_server = 1
innodb_buffer_pool_instances = 48
[myisamck]
a=3
b=4
[sst]
d=3
c=4
# SAFE #
d=0
f=0
Requirement:
The [mysqld]
section and its sections should be pasted above # SAFE #(
section in very few cases section is given as ##SAFE##
or #SAFE#
or # SAFE #
in few of the my.cnf files),Please support.
Expected output:
[client]
a=2
b=7
[mysql]
d=6
e=7
[myisamck]
a=3
b=4
[sst]
d=3
c=4
[mysqld]
disable-log-bin = 1
skip-name-resolve = 1
performance-schema = 0
local-infile = 0
mysqlx = 0
open_files_limit = 200000
max_allowed_packet = 256M
sql_mode="NO_ENGINE_SUBSTITUTION"
#the below are password related of [mysqld]
validate password=1
innodb_dedicated_server = 1
innodb_buffer_pool_instances = 48
# SAFE #
d=0
f=0
Upvotes: -1
Views: 153
Reputation: 3975
$ awk '
/\[mysqld\]/,/\[myisamck\]/{
if($0 !~ / *\[myisamck\] */) {
section=section"\n"$0
next
}
}
/#?# ?SAFE ?##?/{
print section
}1
' my.cnf
Upvotes: 0
Reputation: 34054
Assumptions/Understandings:
my.cnf
file)[mysqld]
block may come before or after the # SAFE #
line; options include storing the entire file in memory structures or making two passes through the file#SAFE#
and # SAFE #
so we'll look for a line with the 3 strings #
/ SAFE
/ #
where they may be white space between the strings# SAFE #
(with or without white space) only occurs once in the fileOne awk
idea requiring two passes through the input file:
awk '
BEGIN { regex_hdr = "^[[:space:]]*" "[[]" "[^]]*" "[]]" "[[:space:]]*$"
regex_safe = "^[[:space:]]*" "#" "[[:space:]]*" "SAFE" "[[:space:]]*" "#" "[[:space:]]*$"
}
{ if ($0 ~ regex_hdr) { # if line looks like "[....]" then ...
if ($1 == "[mysqld]") { # if the header is "[mysqld]" then ...
if (FNR==NR) save = 1 # if 1st file set the "save" flag else ...
else skip = 1 # (2nd file) set the "skip" flag
}
else
save = skip = 0 # (not "[mysqld]") then clear flags
}
if ($0 ~ regex_safe) { # if line looks like "# SAFE #" and ...
if (NR>FNR) # if processing 2nd file then ...
for (i=1; i<=blkcnt; i++) # loop through the block[] array and ...
print block[i] # print the "[mysqld]" block
save = skip = 0 # reset variables
}
if (save) # if 1st file and in the "[mysqld]" block then ...
block[++blkcnt] = $0 # save the line in our block[] array
if ( NR>FNR && !skip ) # if 2nd file and not skipping then ...
print # print current line
}
' my.cnf my.cnf # two copies of file are read/processed
Where:
awk
will concatenate them into a single string during processing)FNR==NR
- is true when processing the 1st fileNR>FNR
- is true when processing the 2nd fileblock[]
array) the [mysqld]
block[mysqld]
block, b) print all other lines to stdout except c) if the line consists of the string # SAFE #
then we'll first print the contents of the block[]
array (ie, the [mysqld]
block) to stdout and then print the # SAFE #
line to stdoutThis generates:
[client]
a=2
b=7
[mysql]
d=6
e=7
[myisamck]
a=3
b=4
[sst]
d=3
c=4
[mysqld]
disable-log-bin = 1
skip-name-resolve = 1
performance-schema = 0
local-infile = 0
mysqlx = 0
open_files_limit = 200000
max_allowed_packet = 256M
sql_mode="NO_ENGINE_SUBSTITUTION"
#the below are password related of [mysqld]
validate password=1
innodb_dedicated_server = 1
innodb_buffer_pool_instances = 48
# SAFE #
d=0
f=0
NOTES:
awk '...' my.cnf my.cnf > tmpfile
) and then b) mv tmpfile my.cnf
GNU awk
it is possible to have awk
overwrite the original file but that will require playing with the inplace
library and toggling some related variables on the command line ... doable but a bit more complicatedssh/EOF
construct is too wieldly, perhaps consider copying the file to the remove host (eg, scp
) before executing (via ssh
) ... ?Upvotes: 2
Reputation: 7781
I don't use sshpass
but plain ol ssh
with key, something like:
#!/usr/bin/env bash
servers=(
server1
server2
server3
...
)
for server in "${servers[@]}"; do
ssh -t "jetchisel@$server" 'ed -s ~/my.cnf <<EOF
/#*[[:space:]]*SAFE[[:space:]]*#*/kx
/^\[mysqld\]$/;/^\[.*/-1m'\''x-1
,p
Q
EOF'
done
To add the content of /home/servers.txt
to an array, one way is to use mapfile
aka readarray
which is a bash4+ feature.
mapfile -t servers < /home/servers.txt
Add w
after the line where ,p
is at to actually write/edit the file in-place
Remove the ,p
to silence the output.
Upvotes: 1