How to run MySQL command on bash? - mysql

The following code works on the command line
mysql --user='myusername' --password='mypassword' --database='mydatabase' --execute='DROP DATABASE myusername;
CREATE DATABASE mydatabase;'
However, it doesn't work on bash file on execution
#!/bin/bash
user=myusername
password=mypassword
database=mydatabase
mysql --user='$user' --password='$password' --database='$database' --execute='DROP DATABASE $user; CREATE DATABASE $database;'
I receive the following error:
ERROR 1045 (28000): Access denied for user '$user'#'localhost' (using password: YES)
How to make the bash file run as the command line?

Use double quotes while using BASH variables.
mysql --user="$user" --password="$password" --database="$database" --execute="DROP DATABASE $user; CREATE DATABASE $database;"
BASH doesn't expand variables in single quotes.

This one worked, double quotes when $user and $password are outside single quotes. Single quotes when inside a single quote statement.
mysql --user="$user" --password="$password" --database="$user" --execute='DROP DATABASE '$user'; CREATE DATABASE '$user';'

I have written a shell script which will read data from properties file and then run mysql script on shell script. sharing this may help to others.
#!/bin/bash
PROPERTY_FILE=filename.properties
function getProperty {
PROP_KEY=$1
PROP_VALUE=`cat $PROPERTY_FILE | grep "$PROP_KEY" | cut -d'=' -f2`
echo $PROP_VALUE
}
echo "# Reading property from $PROPERTY_FILE"
DB_USER=$(getProperty "db.username")
DB_PASS=$(getProperty "db.password")
ROOT_LOC=$(getProperty "root.location")
echo $DB_USER
echo $DB_PASS
echo $ROOT_LOC
echo "Writing on DB ... "
mysql -u$DB_USER -p$DB_PASS dbname<<EOFMYSQL
update tablename set tablename.value_ = "$ROOT_LOC" where tablename.name_="Root directory location";
EOFMYSQL
echo "Writing root location($ROOT_LOC) is done ... "
counter=`mysql -u${DB_USER} -p${DB_PASS} dbname -e "select count(*) from tablename where tablename.name_='Root directory location' and tablename.value_ = '$ROOT_LOC';" | grep -v "count"`;
if [ "$counter" = "1" ]
then
echo "ROOT location updated"
fi

Related

Bash - Break up returned value from MySQL query

I am trying to break up a returned value from a mysql call in a shell script. Essentially what I have done so far is query the database for IP addresses that I have stored in a specific table. From there I store that returned value into a bash variable. The code is below:
#!/bin/bash
# This file will be used to obtain the system details for given ip address
retrieve_details()
{
# get all the ip addresses in the hosts table to pass to snmp call
host_ips=$(mysql -u user -ppassword -D honours_project -e "SELECT host_ip FROM hosts" -s)
echo "$host_ips"
# break up the returned host ip values
# loop through array of ip addresses
# while [ ]
# do
# pass ip values to snmp command call
# store details into mysql table host_descriptions
# done
}
retrieve_details
So this returns the following:
192.168.1.1
192.168.1.100
192.168.1.101
These are essentially the values I have in my hosts table. So what I am trying to do is break up each value such that I can get an array that looks like the following:
arr[0]=192.168.1.1
arr[1]=192.168.1.100
arr[2]=192.168.1.101
...
I have reviewed this link here: bash script - select from database into variable but I don't believe this applies to my situation. Any help would be appreciated
host_ips=($(mysql -u user -ppassword -D honours_project -e "SELECT host_ip FROM hosts" -s));
outer () will convert that in array. But you need to change your IFS (Internal Field Separator) to a newline first.
IFS=$'\n';
host_ips=($(mysql -u user -ppassword -D honours_project -e "SELECT host_ip FROM hosts" -s));
unset IFS;
for i in ${host_ips[#]} ; do echo $i ; done;
to print with key
for i in "${!host_ips[#]}"
do
echo "key :" $i "value:" ${host_ips[$i]}
done
wspace#lw:~$ echo $host_ips
192.168.1.1 192.168.1.100 192.168.1.101
wspace#lw:~$ arr=($(echo $host_ips))
wspace#lw:~$ echo ${arr[0]}
192.168.1.1
wspace#lw:~$ echo ${arr[1]}
192.168.1.100
wspace#lw:~$ echo ${arr[2]}
192.168.1.101
wspace#lw:~$ echo ${arr[#]}
192.168.1.1 192.168.1.100 192.168.1.101
wspace#lw:~$
maybe this is what you want

How to Export & Import Existing User (with its Privileges!)

I have an existing MySQL instance (test), containing 2 databases and a few users each having different access privileges to each database.
I now need to duplicate one of the databases (into production) and the users associated with it.
Duplicating the database was easy:
Export:
mysqldump --no-data --tables -u root -p secondb >> secondb_schema.sql
Import:
mysql -u root -p -h localhost secondb < secondb_schema.sql
I didn't find, however, a straightforward way to export and import users, from the command line (either inside or outside mysql).
How do I export and import a user, from the command line?
Update: So far, I have found manual (and thus error prone) steps for accomplishing this:
-- lists all users
select user,host from mysql.user;
Then find its grants:
-- find privilege granted to a particular user
show grants for 'root'#'localhost';
Then manually create user with the grants listed in the result of the 'show grants' command above.
I prefer a safer, more automated way. Is there one?
One of the easiest ways I've found to export users is using Percona's tool pt-show-grants. The Percona tool kit is free, easy to install, and easy to use, with lots of documentation.
It's an easy way to show all users, or specific users. It lists all of their grants and outputs in SQL format. I'll give an example of how I would show all grants for test_user:
shell> pt-show-grants --only test_user
Example output of that command:
GRANT USAGE ON *.* TO 'test_user'#'%' IDENTIFIED BY PASSWORD '*06406C868B12689643D7E55E8EB2FE82B4A6F5F4';
GRANT ALTER, INSERT, LOCK TABLES, SELECT, UPDATE ON `test`.* TO 'test_user'#'%';
I usually rederict the output into a file so I can edit what I need, or load it into mysql.
Alternatively, if you don't want to use the Percona tool and want to do a dump of all users, you could use mysqldump in this fashion:
shell> mysqldump mysql --tables user db > users.sql
Note: --flush-privileges won't work with this, as the entire db isn't being dumped. this means you need to run it manually.
shell> mysql -e "FLUSH PRIVILEGES"
mysql -u<user> -p<password> -h<host> -e"select concat('show grants for ','\'',user,'\'#\'',host,'\'') from mysql.user" > user_list_with_header.txt
sed '1d' user_list_with_header.txt > ./user.txt
while read user; do mysql -u<user> -p<password> -h<host> -e"$user" > user_grant.txt; sed '1d' user_grant.txt >> user_privileges.txt; echo "flush privileges" >> user_privileges.txt; done < user.txt
awk '{print $0";"}' user_privileges.txt >user_privileges_final.sql
rm user.txt user_list_with_header.txt user_grant.txt user_privileges.txt
Above script will run in linux environment and output will be user_privileges_final.sql that you can import in new mysql server where you want to copy user privileges.
UPDATE: There was a missing - for the user of the 2nd mysql statement.
In mysql 5.7 and later you can use this.
mysqlpump -uroot -p${yourpasswd} --exclude-databases=% --users
This will generate a sql format output that you can redirect to mysql_users.sql.
Note that it is mysqlpump not mysqldump.
Yet another bash one-liner for linux that you can use instead of the Percona tool:
mysql -u<user> -p<password> -h<host> -N mysql -e "select concat(\"'\", user, \"'#'\", host, \"'\"), coalesce(password, authentication_string) from user where not user like 'mysql.%'" | while read usr pw ; do echo "GRANT USAGE ON *.* TO $usr IDENTIFIED BY PASSWORD '$pw';" ; mysql -u<user> -p<password> -h<host> -N -e "SHOW GRANTS FOR $usr" | grep -v 'GRANT USAGE' | sed 's/\(\S\)$/\1;/' ; done
In complement of #Sergey-Podushkin 's answer, this shell script code is workin for me:
mysql -u<user> -p<password> -N mysql -e "select concat(\"'\", user, \"'#'\", host, \"'\"), authentication_string from user where not user like 'root'" | while read usr pw ; do mysql -u<user> -p<password> -N -e "SHOW GRANTS FOR $usr" | sed 's/\(\S\)$/\1;/'; done
PhpMyAdminYou can use phpMyAdmin.
Login and Go to your database or a table where the user has access.
Select privileges
All users with access are there.
Select Export. And a little window with all the GRANTS are there ready to copy and paste.
I tackled this with a small C# program. There is code here to generate a script or apply the grants directly from source to destination. If porting from a Windows -> *nix environment you may have to consider case sensitivity issues.
using System;
using MySql.Data.MySqlClient;
using System.Configuration;
using System.IO;
using System.Collections.Generic;
namespace GenerateUsersScript
{
class Program
{
static void Main(string[] args)
{
List<string> grantsQueries = new List<string>();
// Get A Show Grants query for each user
using (MySqlConnection sourceConn = OpenConnection("sourceDatabase"))
{
using (MySqlDataReader usersReader = GetUsersReader(sourceConn))
{
while (usersReader.Read())
{
grantsQueries.Add(String.Format("SHOW GRANTS FOR '{0}'#'{1}'", usersReader[0], usersReader[1]));
}
}
Console.WriteLine("Exporting Grants For {0} Users", grantsQueries.Count);
using (StreamWriter writer = File.CreateText(#".\UserPermissions.Sql"))
{
// Then Execute each in turn
foreach (string grantsSql in grantsQueries)
{
WritePermissionsScript(sourceConn, grantsSql, writer);
}
//using (MySqlConnection destConn = OpenConnection("targetDatabase"))
//{
// MySqlCommand command = destConn.CreateCommand();
// foreach (string grantsSql in grantsQueries)
// {
// WritePermissionsDirect(sourceConn, grantsSql, command);
// }
//}
}
}
Console.WriteLine("Done - Press A Key to Continue");
Console.ReadKey();
}
private static void WritePermissionsDirect(MySqlConnection sourceConn, string grantsSql, MySqlCommand writeCommand)
{
MySqlCommand cmd = new MySqlCommand(grantsSql, sourceConn);
using (MySqlDataReader grantsReader = cmd.ExecuteReader())
{
while (grantsReader.Read())
{
try
{
writeCommand.CommandText = grantsReader[0].ToString();
writeCommand.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine(grantsReader[0].ToString());
Console.WriteLine(ex.Message);
}
}
}
}
private static void WritePermissionsScript(MySqlConnection conn, string grantsSql, StreamWriter writer)
{
MySqlCommand command = new MySqlCommand(grantsSql, conn);
using (MySqlDataReader grantsReader = command.ExecuteReader())
{
while (grantsReader.Read())
{
writer.WriteLine(grantsReader[0] + ";");
}
}
writer.WriteLine();
}
private static MySqlDataReader GetUsersReader(MySqlConnection conn)
{
string queryString = String.Format("SELECT User, Host FROM USER");
MySqlCommand command = new MySqlCommand(queryString, conn);
MySqlDataReader reader = command.ExecuteReader();
return reader;
}
private static MySqlConnection OpenConnection(string connName)
{
string connectionString = ConfigurationManager.ConnectionStrings[connName].ConnectionString;
MySqlConnection connection = new MySqlConnection(connectionString);
connection.Open();
return connection;
}
}
}
with an app.config containing ...
<connectionStrings>
<add name="sourceDatabase" connectionString="server=localhost;user id=hugh;password=xxxxxxxx;persistsecurityinfo=True;database=MySql" />
<add name="targetDatabase" connectionString="server=queeg;user id=hugh;password=xxxxxxxx;persistsecurityinfo=True;database=MySql" />
</connectionStrings>
Here's what I'm using these days as part of my daily backup scripts (requires root shell and MySQL access, linux shell, and uses the mysql built-in schema:
First, I create a file /var/backup/mysqlroot.cnf containing the root password so I can automate my scripts and not hardcode any passwords in them:
[client]
password=(put your password here)
Then I create an export script which dumps create user commands and grants like this:
touch /var/backup/backup_sql.sh
chmod 700 /var/backup/backup_sql.sh
vi /var/backup/backup_sql.sh
And then write the following contents:
#!/bin/bash
mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe " \
SELECT \
CONCAT( 'CREATE USER \'', User, '\'#\'', Host, '\' IDENTIFIED BY \'', authentication_string, '\'\;' ) AS User \
FROM mysql.user \
WHERE \
User NOT LIKE 'mysql.%' AND CONCAT( User, Host ) <> 'rootlocalhost' AND User <> 'debian-sys-maint' \
"
mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe " \
SELECT \
CONCAT( '\'', User, '\'#\'', Host, '\'' ) as User FROM mysql.user \
WHERE \
User NOT LIKE 'mysql.%' \
AND CONCAT( User, Host ) <> 'rootlocalhost' \
AND User <> 'debian-sys-maint' \
" | sort | while read u ;
do echo "-- $u"; mysql --defaults-extra-file=/var/backup/mysqlroot.cnf -sNe "show grants for $u" | sed 's/$/;/'
done
Then I just have to run it like this:
/var/backup/backup_sql.sh > /tmp/exportusers.sql
A PHP script to loop over your users to get the grant commands would be as such:
// Set up database root credentials
$host = 'localhost';
$user = 'root';
$pass = 'YOUR PASSWORD';
// ---- Do not edit below this ----
// Misc settings
header('Content-type: text/plain; Charset=UTF-8');
// Final import queries goes here
$export = array();
// Connect to database
try {
$link = new PDO("mysql:host=$host;dbname=mysql", $user, $pass);
} catch (PDOException $e) {
printf('Connect failed: %s', $e->getMessage());
die();
}
// Get users from database
$statement = $link->prepare("select `user`, `host`, `password` FROM `user`");
$statement->execute();
while ($row = $statement->fetch())
{
$user = $row[0];
$host = $row[1];
$pass = $row[2];
$export[] = "CREATE USER '{$user}'#'{$host}' IDENTIFIED BY '{$pass}'";
// Fetch any permissions found in database
$statement2 = $link->prepare("SHOW GRANTS FOR '{$user}'#'{$host}'");
$statement2->execute();
while ($row2 = $statement2->fetch())
{
$export[] = $row2[0];
}
}
$link = null;
echo implode(";\n", $export);
Gist: https://gist.github.com/zaiddabaeen/e88a2d10528e31cd6692
pass=your_password_here; \
MYSQL_PWD=$pass mysql -B -N -uroot -e "SELECT CONCAT('\'', user,'\' ','\'', host,'\' ','\'', authentication_string,'\' ','\'', plugin,'\'') FROM mysql.user WHERE user != 'debian-sys-maint' AND user != 'root' AND user != 'mysql.sys' AND user != 'mysql.session' AND user != ''" > mysql_all_users.txt; \
while read line; do linearray=(${line}); \
MYSQL_PWD=$pass mysql -B -N -uroot -e "SELECT CONCAT('CREATE USER \'',${linearray[0]},'\'#\'',${linearray[1]},'\' IDENTIFIED WITH \'',${linearray[3]},'\' AS \'',${linearray[2]},'\'')"; \
done < mysql_all_users.txt > mysql_all_users_sql.sql; \
while read line; do linearray=(${line}); \
MYSQL_PWD=$pass mysql -B -N -uroot -e "SHOW GRANTS FOR ${linearray[0]}#${linearray[1]}"; \
done < mysql_all_users.txt >> mysql_all_users_sql.sql; \
sed -e 's/$/;/' -i mysql_all_users_sql.sql; \
echo 'FLUSH PRIVILEGES;' >> mysql_all_users_sql.sql; \
unset pass
First mysql command : export all users to file and exclude some.
Second mysql command : loop users from file to write a sql command 'create user' to an exported file (with authentication credentials).
Third mysql command : loop users from file to append their privileges to the exported file.
sed command to append a ";" to end of lines and flush privileges to finish.
To import : MYSQL_PWD=$pass mysql -u root < mysql_all_users_sql.sql
SELECT CONCAT('\create user ', user,'\'#\'', host, '\' identified by ', "'", authentication_string, "'"'\;') FROM user WHERE user != 'mysql.session' AND user !='mysql.sys' AND user != 'root' AND user != '';
I had the same problem. The solution is that after the import of the backup you need to do a "flush privileges;". Then the privileges of the users will be active as in the original database.
So execute:
mysql -u root -p -h localhost secondb < secondb_schema.sql
mysql -u root
then in mysql:
flush privileges;

Catching exit code after executing mysql query

I'm working on a bash script that will query mysql. I would like to add some error checking. Let say if the following query for some reason fails I want to catch the exit error and exit the script. For example this is part of my script.
QUERY="SELECT DISTINCT `TABLE_SCHEMA`, `TABLE_NAME`
FROM `information_schema`.`TABLES`
WHERE table_schema NOT IN ( 'mysql', 'information_schema', 'performance_schema' )"
mysql -u user -pPASSWD --batch -N -e "$QUERY" | while read DATABASE TABLE;
do
...
...
...
done
How could i catch the exit code after the scripts run the "$QUERY". I was thinking something like this. But it doesn't seem to work.
mysql -u user -pPASSWD --batch -N -e "$QUERY" echo $? | while read DATABASE TABLE;
Any ideas
You are in the good way: $? is the flag to check:
$ mysql -h mydb <<< "SELECT * FROM MyDB.some_table_that_exists;"
$ echo $?
0
$ mysql -h mydb <<< "SELECT * FROM MyDB.asdfasdfasdf;"
ERROR 1146 (42S02) at line 1: Table 'MyDB.asdfasdfasdf;' doesn't exist
$ echo $?
1
So what you can do is to execute the query and then:
if [ $? ... ]; then ...

DATABASE autocreation bash script: What's wrong?

My bash script won't crate the Database. What am I doing wrong here?
Please have a look:
#!/bin/bash -x
set -x
function deebee() {
EXPECTED_ARGS=2
E_BADARGS=65
MYSQL=`which mysql`
Q1="CREATE DATABASE IF NOT EXISTS $1;"
Q2="GRANT USAGE ON *.* TO $2#localhost IDENTIFIED BY '$3';"
Q3="GRANT ALL PRIVILEGES ON $1.* TO $2#localhost;"
Q4="FLUSH PRIVILEGES;"
SQL="${Q1}${Q2}${Q3}${Q4}"
if [ $# -ne $EXPECTED_ARGS ]
then
echo "Usage: $0 dbname dbuser dbpass"
exit $E_BADARGS
fi
$MYSQL -uroot -p -e "$SQL"
}
deebee $1 $2 $3
I'm calling the script as I've put it in a function, but it just spits out the expected arguments telling me the syntax, i.e that I should type in the bashscrip name, dbname, dbuser dbpass, but obviously there' something wrong with the script of my login permissions or user so that I can't automate this... What's going on, I'd love to know!
Thanks!
What I would do :
creating a mysql config file with credentials (easier to automate now) :
cat ~/.my.cnf
[client]
host = localhost
user = root
password = xxx
And the script :
#!/bin/bash
set -x
deebee() {
EXPECTED_ARGS=3
E_BADARGS=65
MYSQL=$(type -p mysql)
if (($# != $EXPECTED_ARGS))
then
echo "Usage: $0 dbname dbuser dbpass"
exit $E_BADARGS
fi
$MYSQL <<EOF
CREATE DATABASE IF NOT EXISTS '$1';
GRANT USAGE ON *.* TO '$2'#'localhost' IDENTIFIED BY '$3';
GRANT ALL PRIVILEGES ON '$1'.* TO '$2'#'localhost';
FLUSH PRIVILEGES;
EOF
}
# TODO tests on input args
deebee "$1" "$2" "$3"
And like Denis said in comments, take care of what users can put as arguments to prevent sql injection.
You should add some tests on input args.

How can I drop all the views from a MYSQL Database?

My database had lots of views and it was impossible to drop them one by one.
I would like to just drop them all because the database doesn't refresh structure changes of the tables in the view that select from them.
If you want to do this in the MySQL client, you can dynamically generate the DDL statements using information_schema, dump them to a SQL script, and then execute that script.
Example:
select concat('drop view ',table_schema,'.',table_name,';') as ddl
into outfile '/tmp/drop_all_views.sql'
from information_schema.views
where table_schema = 'your_schema';
\. /tmp/drop_all_views.sql
After searching on the web, I found a shell script to drop all tables here: http://www.cyberciti.biz/faq/how-do-i-empty-mysql-database/
Then, I changed that script to drop all views of the database.
This is the final result:
#!/bin/bash
PREFIX=""
SUFFIX=""
HOST="localhost"
PORT="3306"
while getopts p:s:h:P: OPCAO; do
case "${OPCAO}" in
p) PREFIX="${OPTARG}" ;;
s) SUFFIX="${OPTARG}" ;;
h) HOST="${OPTARG}" ;;
P) PORT="${OPTARG}" ;;
esac
done
shift $((OPTIND-1))
MUSER="$1"
MPASS="$2"
MDB="$3"
# Detect paths
MYSQL=$(which mysql)
AWK=$(which awk)
GREP=$(which grep)
if [ $# -eq 0 ]
then
echo "Usage: $0 [-h Host] [-P Port] [-p Prefix-View-Name] [-s Suffix-View-Name] {MySQL-User-Name} {MySQL-User-Password} {MySQL-Database-Name}"
echo "Drops all views from a MySQL"
exit 1
fi
TABLES=$($MYSQL -h $HOST -P $PORT -u $MUSER -p$MPASS $MDB -e "SELECT table_name FROM information_schema.views WHERE table_schema = '$MDB' AND table_name LIKE '$PREFIX%$SUFFIX';" | $AWK '{ print $1}')
for t in $TABLES
do
echo "Deleting $t view from $MDB database..."
$MYSQL -h $HOST -P $PORT -u $MUSER -p$MPASS $MDB -e "drop view $t"
done
This solution will work just for who uses Unix-like systems.
To call the script, I used:
./script.sh [-h host] [-P port] [-p prefixViewName] [-s suffixViewName] username password databaseName
EDIT: I improved the script to accept option parameters, like the host, port and also a prefix and a suffix to filter what views will be dropped.