I've the following bash script to upgrade my database schema. Script reads hostname and database password from command line.
The problem is here that if the password is alphanumeric e.g r00t then script works. But if password contains special characters e.g pa**w0rd, then script does not work and directly exits. Please help me with this. Thanks.
#!/bin/bash
echo "Enter hostname."
read -p "Hostname [localhost]: " DB_HOST
DB_HOST=${DB_HOST:-localhost}
echo "Enter MySQL root password"
DB_PASS=
while [[ $DB_PASS = "" ]]; do
read -sp "Password: " DB_PASS
done
MYSQL="mysql --force --connect-timeout=90 --host=$DB_HOST -u root --password=${DB_PASS}"
# Apply schema updates. My DBName is "mydb"
# Upgrade schema file is stored in "mysql" folder
$MYSQL mydb -e exit > /dev/null 2>&1 && $MYSQL mydb < "../mysql/upgrade_schema_v.2.1.sql"
Logging into mysql using bash
For ubuntu or linux shell try to use command
mysql -u username -p'p#ssw()rD'
for remote host login use
mysql -h hostname -u user -p'password'
This is occurring because you are using shell GLOB (wildcard) characters in the password, and in Bash (or on Linux generally) wildcards are expanded by the shell.
The safest and most reliable solution is to not use shell wildcard characters or other characters interpreted by the shell in the password. You should also avoid spaces. There are plenty of other characters.
Here are the ones you should avoid:
" ' $ , [ ] * ? { } ~ # % \ < > | ^ ;
Here are the ones it is usually safe to use:
: # . , / + - ! =
To ensure the password is still secure, make it longer. As an example:
K#3amvv7l1wz1192sjqhym
This meets old-fashioned password complexity rules, because upper, lower, numbers and special characters are in the first four, the remainder is randomly generated but avoids any problematic characters.
However if you must use them, you can quote the password parameter with single quotes - though you will still run in to trouble if the password contains single quotes!
Try enclosing your password in single quotes.
If it's pa**w0rd, use 'pa**w0rd'
Variables are best used for data, not code. The layers of variables make it hard to protect the expansion when you want some parts of the expansion (i.e., you want your command line to be word split on the tokens you want), but don't want all the other effects. The solution is to not store the code in a string. Instead, use a function like:
do_mysql() {
host="$1"
pass="$2"
mysql --force --connect-timeout=90 --host="$host" -u root --password="$pass" "$#"
}
then you can run it with extra arguments like
do_mysql "$DB_HOST" "$DB_PASS" -e exit > /dev/null && do_mysql "$DB_HOST" "$DB_PASS" < "../mysql/upgrade_schema_v.2.1.sql"
Though it would also be better not to use upper case for your variables. Doing so makes it so you could collide with environment variables and accidentally change things you don't intend to change (as the number of people who accidentally reset PATH can attest).
Related
I what to write a shell script to loging in to mariadb. The shell script read one password containing special characters(blank, !#) in a ini file.
The OS is Ubuntu 18.04
the ini file as follows:
user=xxx-xxx-xxx
password=xxx /xxx /xx/ !\#
the shell script as follows:
#!/bin/bash
baseDir="$(cd "$(dirname "$0")" && pwd)"
iniPath="$baseDir/backup.ini"
echo "iniPath is $iniPath"
dbUser="$(grep 'user' $iniPath | cut -d '=' -f 2)"
echo "user is $dbUser"
dbPassword="$(grep 'password' $iniPath | cut -d '=' -f 2)"
echo "password is $dbPassword"
mysql -h localhost -u $dbUser -p'$dbPassword'
if I input the command as follows:
mysql -h localhost -u xxxxxx -p'xxx /xxx /xx/ !#'
in command line, it loging successfully.
But If I execute the shell script, it always results in accessing denied for user.
Have any suggestions? thanks.
Have you tried to use: mysql -h localhost -u $dbUser -p'echo $dbPassword' ? (special character ` is on US like keyboards under esc key left upper corner, it looks like back apostroph) Looks like the variable with password is not correctly "printed" into a mysql command before its run. Other way I would recommend trying is to use -p"$dbPassword"
FWIW, the issue is that the shell will not interpolate variables into a string surrounded by single quotes. As Honza specified, the double-quotes will work.
See Difference between single and double quotes in Bash for details.
I am trying to run an ssh command in my shell script which will automatically create a database, user, password etc on a remote server:
password=`date +%s|sha256sum|base64|head -c 32`
read -p "Enter staging folder name... e.g. xxxxxxxx: " stagingdirectory
echo $stagingdirectory
read -p "Give the name for the database (this will be used in mysql)" dbname
echo $dbname
read -p "Give the name of the user for the database (this will be used in mysql)" dbuser
echo $dbuser
sqlstatement="mysql -uXXXXXXX -pXXXXXXXX -hXXXXXXXX -e "
sqlstatement+='"CREATE DATABASE IF NOT EXISTS $dbname;CREATE USER $dbuser#'%' IDENTIFIED BY '$password';GRANT ALL PRIVILEGES ON $dbname.* TO $dbuser#'%';FLUSH PRIVILEGES;"'
echo $sqlstatement
ssh -A $domainname#35.163.55.55 -e "$sqlstatement"
When I try and run this command, I get this error:
This is what gets returned (I have replaced actual values with XXXX):
Bad escape character 'mysql -udbadmin -pXXXXX -hXXXXX -e"CREATE DATABASE IF NOT EXISTS $dbname;CREATE USER $dbuser#% IDENTIFIED BY XXXXXX;GRANT ALL PRIVILEGES ON $dbname.* TO $dbuser#%;FLUSH PRIVILEGES;"'.
I think this is due to my sql statement and strings escaping.
Try changing the line,
ssh -A $domainname#35.163.55.57 -e "$sqlstatement"
to
ssh -A $domainname#35.163.55.57 "$sqlstatement"
From the manual of ssh, see below
-e escape_char
Sets the escape character for sessions with a pty (default: ‘~’). The escape character is only recognized at the
beginning of a line. The escape
character followed by a dot (‘.’) closes the connection; followed by control-Z suspends the connection; and followed by itself
sends the escape char-
acter once. Setting the character to “none” disables any escapes and makes the session fully transparent.
Here in your example, ssh is not getting a valid escape character, and you don't need one either
I want to run mysql client, giving the password non-interactively.
The standard solution is this
mysql -u root -e "foo" -p < password_file
but my situation is this
produce_password | mysql -u root -p
Here, mysql prompts for a password, even though data is being piped in. (Yes, produce_password is emitting data; echo foo | mysql behaves the same way.)
The Internet seems to think the above should work, but the fact is it doesn't. The workaround would be
produce_password > password_file
mysql -u root -p < password_file
rm password_file
But let's say I don't want to do this (e.g. policy demands that this password never be written to the disk)
How can I make mysql take the password from the input process without prompting, as it would for a file?
With thanks to fancyPants for explaining the cause, here is a solution which meets my requirements. (The encrypted .mylogin.cnf with mysql_config_editor isn't right for me, but thanks.)
To satisfy the security policy, mount /ramfs as a ramfs temporary file system. Assume file permissions are suitably restrictive.
ramdir="/ramfs"
cnf="$(mktemp "$ramdir/this-script-name-XXXXX")"
pw="$(produce_password)"
cat >"$cnf" <<EOF
[client]
user=root
password="$pw"
EOF
mysql --defaults-extra-file="$cnf" -e 'select 1'
rm "$cnf"
the problem with that is the password shows up in the process list.
But you can do this.
mysql --defaults-file=<(echo '[client]'; echo 'user=USERNAME'; echo "password=$mysqlpassword";)
It shows up in the process list like this.
mysql --defaults-file=/dev/fd/63
so the <() creates a file handle to the output of your commands.
This works with command line options that are expecting a file.
I don't know how to explain this, but when you pipe something, the stdout of the first program is forwarded to the stdin of the second program. You somehow confuse this, with the command line or whatever. You can pipe something to mysql, of course, but whatever you pipe is handled after you've authenticated yourself.
The solution would be
mysql -u root -p$(produce_password)
This generates your password with whatever program you have there and puts it in the right place on your commandline (my english is bad, can't explain better). When you have it in a file you could do
mysql -u root -p$(cat file)
I don't know, why you want to do it this way anyway, but you might be interested in having an encrypted file with your credentials, that you can use to log in without specifying a password. Read more about it here.
The easiest way is to declare the environment variable MYSQL_PWD.
Example:
$ export MYSQL_PWD=$(produce the password if required)
$ mysql -h example.org -u username
Remember you should not use -p in this case.
I read the following snippet:
mysql -u $USER -p $PASS <<EOF 2> /dev/null
CREATE DATABASE students;
EOF
Ok. So this runs the mysql client and does a CREATE DATABASE.
My question is how come the 2>/dev/null is not considered part of the EOF?
I assume that the << is for the input to the program mysql after it has connected with the user and password parameters.
So how 2>/dev/null is not part of the <<?
Because words in the shell are separated by whitespace. The here-doc terminator is a word, so it does not consume the rest of the line.
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Operation
Your example uses a Heredoc, and the shell ultimately interprets this as a redirection in itself. It is treated like other forms of redirection.
Equivalent Examples:
Herestring:
mysql -u $USER -p $PASS<<<"CREATE DATABASE students;" 2>/dev/null
Echo:
echo "CREATE DATABASE students;" | mysql -u $USER -p $PASS 2>/dev/null
I shamelessly recommend reading further at Greg's Wiki, because it is an excellent resource:
http://mywiki.wooledge.org/BashGuide/InputAndOutput
I'm trying to execute this:
$ mysql --user=XXX --password=XXX --batch --skip-column-names \
-e "SELECT userid, displayname FROM Users" stackoverflowdb | \
split -l 50 -a 5 - "result."
but bash complains about ) 'unexpected' character (i have this character and few other 'weird ones' in my mysql password). I tried to take my password in quotes --password="myweirdpasshere" but then mysql won't login me at all (probably password is incorrect?)
Some characters such as $, `, ", and sometimes \ retain their special meaning inside double quotes. If your password contains any of those characters, use single quotes instead. (Unless your password also contains single quotes, in which case you might just drop the quotes altogether and put a \ before each special character.)
You should be able to put the password in the $HOME/.my.cnf file, avoiding this issue as well as increasing the security.
Another option to using $HOME/.my.cnf is embedding the configuration file into the script. A comment on the documentation gives examples on this. For your case it should be:
$ mysql --user=XXX --defaults-file <(printf '[client]\npassword=XXX\n') \
--batch --skip-column-names \
-e "SELECT userid, displayname FROM Users" stackoverflowdb | \
split -l 50 -a 5 - "result."
If you don't give the password on the command line, it will bring up an interactive prompt. That might solve your error. Just use -p instead of --password=XXX.
Of course, if you require unattended access to the script, that's not a very useful answer. It would be more helpful if you could create a minimal example and tell us the exact error that Bash gives you.