Text processing experiments for finding the MySQL configuration files
When it comes to configuring MySQL, a fundamental step is to find out which configuration files the MySQL server reads.
The operation itself is simple, however, if we want to script the operation, using text processing in a sharp way, it’s not immediate what the best solution is.
In this post I’ll explore the process of looking for a satisfying solution, going through grep, perl, and awk.
Contents:
- Assumptions
- Input data (finding the configuration files read by MySQL)
- First step: grep+tail
- Second step: expanding the tilde
- Final step: awk’s super powers
- Extra step: using the output
- Conclusion
Assumptions
For simplicity, we assume that the filenames returned by the mysqld
commands, and the user home path, don’t require quoting (e.g. have spaces).
Input data (finding the configuration files read by MySQL)
Finding the configuration files is a simple operation:
$ mysqld --verbose --help
This yields a pages-long text, with all the command lines parameter and the server configuration; the relevant section is:
# ...
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
# ...
First step: grep+tail
A generic, manual, approach is to use grep to isolate the text:
$ mysqld --verbose --help | grep -A 1 "^Default options"
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
Using the option -A
(--after-context
), we tell grep to print the given number of lines after the match.
Now we isolate the options line:
$ mysqld --verbose --help | grep -A 1 "^Default options" | tail -n 1
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
Standard approach - we use tail -n 1
in order to print the last 1 line(s).
Second step: expanding the tilde
There’s a problem now; we need to expand the tilde (~
).
Since the string ~/.my.cnf
is the output of a command, it’s not expanded by the subshell; this simplified example fails:
$ ls -l $(echo '~/.my.cnf')
ls: cannot access '~/.my.cnf': No such file or directory
We’ll try search/replace the tilde with the home path ($HOME
in any shell) via Perl:
$ mysqld --verbose --help | grep -A 1 "^Default options" | tail -n 1 | perl -pe "s/~/$HOME/g"
Unknown regexp modifier "/h" at -e line 1, at end of line
syntax error at -e line 1, at EOF
Execution of -e aborted due to compilation errors.
Yikes! What happened?
The problem is that $HOME
, in my case /home/saverio
, contains backslashes, which are interpolated by the shell, and ultimately interpreted by Perl; this is the simplified example:
$ echo perl -pe "s/~/$HOME/g"
perl -pe s/~//home/saverio/g
$ echo | perl -pe 's/~//home/saverio/g'
Unknown regexp modifier "/h" at -e line 1, at end of line
Execution of -e aborted due to compilation errors.
which causes the error previously raised.
Perl can access environment variables - this comes to our rescue:
$ echo '~/.my.cnf' | perl -pe 's/~/$ENV{"HOME"}/'
/home/saverio/.my.cnf
We now have the building blocks of a fully functional command:
$ mysqld --verbose --help | grep -A 1 "^Default options" | tail -n 1 | perl -pe 's/~/$ENV{"HOME"}/g'
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf /home/saverio/.my.cnf
Don’t forget the /g
regex modifier! It tells Perl to replace all the occurrences of a pattern in each matching line, if there’s more than one match (per line).
Our task is now accomplished. Can we do better?
Final step: awk’s super powers
While the last revision of the command works, it contains way too many commands. Does the GNU toolbox have better tools?
Let’s see what awk offers.
Awk is a (Turing-complete!) programming language, dedicated to text-processing; hopefully, it includes built-in functions relevant to our task.
The ugliest part right now is to isolate the options string from the entire mysqld
help. The logic required is:
- find a matching line
- print the line below
with grep, unfortunately we can’t just print the line below without printing the matching line. But we can with awk!:
$ mysqld --verbose --help | awk '/^Default options/ { getline; print }'
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
Awk’s language is fortunately fairly intuitive.
We use pattern matching /<pattern>/
to match the intended line, and for the matches we execute a block ({ ... }
) that goes to the next line (getline
) and then prints the current one (print
).
Now, in the current revision, we still have two commands, awk
and perl
:
mysqld --verbose --help | awk '/^Default options/ { getline; print }' | perl -pe 's/~/$ENV{"HOME"}/g'
Let’s merge them! We use awk’s search and replace, and environment variables access:
$ mysqld --verbose --help | awk '/^Default options/ { getline; gsub("~", ENVIRON["HOME"]); print }'
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf /home/saverio/.my.cnf
Here we use the search and replace function (gsub(source[, destination[, how]])
; how
is not relevant to this article) and associative arrays applied to environment variables (ENVIRON[<variable_name>]
).
Note that gsub
is the global version of search/replace; it replaces all the occurrence in a string, like perl /g
regex modifier.
Extra step: using the output
As extra step, we want to use the output. Say, let’s add a comment to the [mysqld]
block:
$ perl -i -pe 's/^(\[mysqld\]\n)/# Server configuration group follows:\n$1/' $(mysqld --verbose --help | awk '/^Default options/ { getline; gsub("~", ENVIRON["HOME"]); print }') 2> /dev/null
We just ignore the errors (due to file(s) not found), by sending them to /dev/null
.
Conclusion
Long ago, I thought that one could improve text processing tools with a straight read of educational material. Nowadays, I find much more effective (and pleasant) instead, to try finding out, when I have the opportunity, which are the most effective tools to a accomplish a task.
In this article we’ve done an iterative search of the best text processing tools for the given use case; we’ve found that awk compactly, yet intuitively, satisfies the requirements, and we’ve explored a few, interesting and useful, features along the way.