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
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'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).
I am trying to create a bash script that uses mysqldump to create a backup of the database that is specified as parameter. However mysqldump fails with an access denied error. Using the same command directly (copying it to the shell an executing it) works without any problem.
#!/bin/bash
# ... use parameters to get db name and password
# build the mysqldump command and execute it...
command="mysqldump -alv -h127.0.0.3 --default-character-set=utf8 -u ${database} -p'${pw}' --extended-insert ${database} | gzip > ${path}"
echo "$command"
echo ""
$command
This gives me the following output:
$ ./dbbak DBUSER DBNAME PASSWORD
mysqldump -alv -h127.0.0.3 --default-character-set=utf8 -u DBUSER -p'PASSWORD' --extended-insert DBNAME | gzip > /path/to/backup/backup.sql.gz
Warning: Using a password on the command line interface can be insecure.
-- Connecting to 127.0.0.3...
mysqldump: Got error: 1045: Access denied for user 'DBUSER'#'localhost' (using password: YES) when trying to connect
As said before: When I copy the echoed mysqldump command and execute it directly, the backup works just fine.
What is the problem here? Since the command is executed correctly when being used manually all parameters (password, username, etc.) seem to be correct. Additionally the bash script is executed with the same user account as the manual command.
So why does the manual execution work while the bash script fails?
EDIT:
As Jens pointed out in his comment, removing the quotes from the password will solve the problem. ...-p${pw}... will work, BUT this will also lead to a new problem, if the password contains special characters like $ < > ...
I assume that the problem with the quotes is how bash parses the string. Meanwhile I found some docs that say, that it is a bad habit to store commands in variables and execute them. Instead one should execute commands directly. However the following does not work as well:
result=$(mysqldump -alv -h127.0.0.3 --default-character-set=utf8 -u ${database} -p'${pw}' --extended-insert ${database} | gzip > ${path})
When executing this with bash -x dbbak the output shows the problem:
...
++ mysqldump -alv -h127.0.0.3 --default-character-set=utf8 -u DBUSER '-p'\''DBPASS'\''' --extended-insert DBNAME
While I do understand why the quotes around DBPASS are added ('DBPASS' --> \''DBPASS'\'), I do not understand why there are also quotes around-p`.
How do I get rid of these quotes when executing the command?
You can either:
store the password in an environment variable MYSQL_PWD
store the password in a plain-text file .my.cnf which you need to put into
the home directory of the user that executes the script
use the mysql_config_editor utility to store the password in an encrypted
file
The first one is the easiest to use/implement but obviously the least secure.
I recommend to take a look at the documentation where all the possibilities are described. ;)
Configure it by .cnf file and provide it in --defaults-file
mysqldump --defaults-file=~/my_mysql.cnf db table > table.sql
In ~/my_msyql.cnf
[mysqldump]
user=user_name
password=my_password
host=my_host
This is also safe if you version this. You can save my_mysql.cnf differently per environment.
To remove the single quotes around the password solved for me.
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
Question Rewritten:
HOMEDIR="ftpuser"
REMOTEIP="1.1.1.1"
MYSQLPASS="password"
Q1="DROP USER "$HOMEDIR"_shop;"
Q2="DROP DATABASE "$HOMEDIR"_shop;"
Q3="CREATE DATABASE IF NOT EXISTS "$HOMEDIR"_shop;"
Q4="GRANT ALL ON "$HOMEDIR"_shop TO '"$HOMEDIR"_shop'#'localhost' IDENTIFIED BY '$MYSQLPASS';"
Q5="GRANT ALL ON "$HOMEDIR"_shop TO '"$HOMEDIR"_shop'#'anotherip' IDENTIFIED BY '$MYSQLPASS';"
# Need to grant permissions from another server as well
Q6="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}${Q5}${Q6}"
echo $SQL
echo " "
ssh -p 8899 root#$REMOTEIP "mysql -u root -p "$SQL""
I then run:
/root/testing/migratesite.sh
And get:
bash: DROP: command not found
bash: CREATE: command not found
bash: GRANT: command not found
bash: GRANT: command not found
bash: FLUSH: command not found
What am I missing?
You are missing quotes and a proper mysql client command line:
ssh -p 8899 root#$REMOTEIP "mysql -u root -p -e \"$SQL\""
You need to escape the quotes around the $SQL variable so they get passed to the remote shell, else they get interpreted by the local shell (that's why you get DROP: command not found, the semi colon is interpreted by the shell.) Also, to have the mysql client to execute a command you have to pass the -e command line option.
Did you try this:
ssh -p 8899 root#$REMOTEIP "echo \"$SQL\" | mysql -u root --password=$SQL_PASS"