export mysql table data to csv using bash script - mysql

I am trying to export my sql table data to csv but have these errors. I am really confused with double quotes and single quotes.
#!/bin/bash
mysql -u root -pH0tjava1 -B -e 'SELECT CONCAT("sshpass -p ""Password"" rsync -avvtzh -e ""ssh -o StrictHostKeyChecking=no"" --log-file=""/home/toor/rsync2.log""", login,"#", ftp_addr, " :", camera_name,"/", "/",`\ 'home`',"/",login, "/", camera_name) INTO OUTFILE '/tmp/rsynctest3.csv' lines terminated by '\r\n' from inteliviz.cameras;"
Errors:
/usr/local/bin/rsync.sh: line 8: syntax error near unexpected token `)'
/usr/local/bin/rsync.sh: line 8: ` mysql -u root -pH0tjava1 -B -e "select CONCAT ("sshpass -p "Pa55word" rsync -avvtzh -e "ssh -o StrictHostKeyChecking=no" --log-file= "/home/toor/rsync2.log", login,camera_name,ftp_addr) INTO OUTFILE '/tmp/rsynctest3.csv' lines terminated by '\r\n' from inteliviz.cameras;"'
/usr/local/bin/rsync.sh: line 8: unexpected EOF while looking for matching ``'
/usr/local/bin/rsync.sh: line 11: syntax error: unexpected end of file

I am really confused with double quotes and single quotes.
You have not only double and single quotes, but also backticks in your statement. The mismatches are:
You begin the SQL statement with 'SELECT… and end it with …inteliviz.cameras;". To switch the quotes from ' in the first part to " in the last part, you must insert a closing ' and an opening " betwixt, e. g. …camera_name)'" INTO OUTFILE…
The expression `\ 'home`' is messed - the first ` is inside the outer single quotes and thus has no special function, the ' before home closes the single quoted part, the second ` opens an unclosed command substitution, inside of which the final ' is. You have to reconsider what you want to have there and get the quote nesting straight.

Try with the below script, which works for me:
# Read query into a variable
sql="$(cat query.sql)"
# If sqlplus is not installed, then exit
if ! command -v sqlplus > /dev/null; then
echo "SQL*Plus is required..."
exit 1
fi
# Connect to the database, run the query, then disconnect
echo -e "SET PAGESIZE 0\n SET FEEDBACK OFF\n $sql" | \
sqlplus -S -L "$USERNAME/$PASSWORD#(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=$HOST)(PORT=$PORT))(CONNECT_DATA=(SERVICE_NAME=$DATABASE)))"

Related

Bash string continuation for MySQL command

I'm trying to clean up a database while running some tests in a bash script, and the following attempt at line continuation is not working:
mysql -e "DELETE FROM test.users WHERE username ="\
"'<a href=https://localhost>XSS Hack!</a>';"
I get the error
ERROR 1044 (42000): Access denied for user 'web'#'localhost' to database ''<a href=https://localhost>XSS Hack!</a>';'
The command works fine if I run it on a single line.
The backslash escapes the newline, but the whitespace at the beginning of the next line still acts as a word delimiter. So the command becomes equivalent to:
mysql -e "DELETE FROM test.users WHERE username =" "'<a href=https://localhost>XSS Hack!</a>';"
If you end a line in the middle of a string, you don't need to escape the newline. MySQL doesn't mind newlines in queries, either. So you can simply write:
mysql -e "DELETE FROM test.users WHERE username =
'<a href=https://localhost>XSS Hack!</a>';"

Executing SQL queries using a file in special format

I am trying to write a script that reads lines from a file containing:
a filename of an SQL script, and
some SQL queries on the same line
and launches a query.
There is a list.txt file in the following format:
query.sql; set #var:='prod';
where query.sql is a file with SQL queries, e.g.:
select *
from db
where system=#var
I am trying to write a Bash script that executes the queries like this:
mysql -uroot -proot -e
"set #var:='prod';
select *
from db
where system=#var;"
Here is what I have tried so far:
while read line;
do app=$(echo $line | awk '{for (i=2; i <= NF; i++) printf FS$i; print NL }';
cat `echo $line | awk -F\; '{print $1}'`; echo ";");
mysql -uroot -proot -e "`echo $app`"
done < list.txt
But I am having an SQL error, because the shell does not escape well the * character in the query.sql.
Debugging my code I obtain:
$echo $app
$set #var:='prod'; select a list.txt mysql query1.sql query.sql from db where system=#var ;
How can I adjust the code in order to have
$echo $app
$set #var:='prod'; select * from db where system=#var ;
?
Further details
There is a loop in the awk command, because the file list.txt may contain multiple queries, e.g.:
query.sql; set #var:='prod'; #a=asd; #b=zxc, #...=...;
I don't think you need to read the SQL files line by line. Assuming that there are no semicolons in the filenames, you can construct the query with the following AWK command:
awk -F\; '{
for (i = 2; i <= NF; i++) {
if ($i != "") printf("%s;", $i);
}
printf("source %s;\n", $1)
}' list.txt | mysql -uroot -proot
The command uses ; as a field separator. The for loop prints all fields starting from the second. The last printf function builds a query sourcing the SQL file.
The Cause of the Error
Your code fails to pass the correct SQL to the MySQL client because of this line:
mysql -uroot -proot -e "`echo $app`"
The shell interprets the special characters within the $app variable. In particular, the * character is expanded to "every file in the current directory". To prevent interpreting, use weak quoting, i.e. enclose the variable in double quotes: "$app":
When referencing a variable, it is generally advisable to enclose its name in double quotes. This prevents reinterpretation of all special characters within the quoted string -- except $, (backquote), and \ (escape)...
Keeping $ as a special character within double quotes permits referencing a quoted variable ("$variable"), that is, replacing the variable with its value
Also, there is no need for subshell expression (the backticks and the echo), if you want to get the value of the variable:
mysql -uroot -proot -e "$app"

Load CSV file with bash script gives error although none are thrown when launching mysql command on terminal

I'm trying to load some csv files by calling mysql from the terminal without entering mysql interpreter.
I created the following function which I call when I'm ready to load all csv files mentioned in "$#"
function sqlConn {
sqlLoad="$sqlConnBase $# $dbName"
`"$sqlLoad"`
#I tried simply with $sqlLoad too but same problem occurs,
#although everything needed for the query is present in either
#$sqlLoad or "$sqlLoad"
}
sqlConnBase and dbName are global variables defined at the beginning of my bash script like this:
sqlConnBase="mysql -h localhost -u group8 --password=toto123"
dbName="cs322"
I call sqlConn like this:
sqlConn " --local-infile=1 < sqlLoadFile.sql"
the content of sqlLoadFile.sql is the following:
LOAD DATA LOCAL INFILE 'CSV/notes_rem.csv'
INTO TABLE Notes
CHARACTER SET UTF8
FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\'
LINES TERMINATED BY '\n' STARTING BY '';
The problem I get is the following:
./loadAll.bash: line 31: mysql -h localhost -u group8
--password=toto123 --local-infile=1 < sqlLoadFile.sql cs322: command not found
the strange thing is that when I simply execute
mysql -h localhost -u group8 --password=toto123
--local-infile=1 < sqlLoadFile.sql cs322
on my terminal it does populate my cs322 database, i.e. all the rows of my csv are present in my cs322 database.
What could be the source of the error in my script?
The mysql -h localhost ... is treated as a command and not just mysql where the rest is arguments.
You need to use eval instead of the backticks:
eval "$sqlLoad"
When that is said you should be really careful with escapes, word splitting and globbing, and the above approach should be avoided.
A recommended approach is to populate an array with arguments:
declare -a args
args+=("-h" "localhost")
args+=("-u" "group")
# ...
mysql "${args[#]}"

Unexpected $'\r': command not found after mysql query in shell

This scipt gives proper result, but also gives mistake:$'\r': command not found in line with query.
#!/bin/bash
keyOrPass=$1
intercom=$2
flat=$3
number=$4
mysql -ulogin -ppass dbname -e "select cli.codeGuestEmail, cli.codePrivateEmail, cliKey.rf_id, cliKey.emailNotification from mbus_clients as cli join mbusClientKeys as cliKey on cliKey.id_client=cli.id WHERE cli.flat=${flat} and cli.domophone=${intercom};";
This is how I run the script:
sh sendEmailNotification.sh key 10001014 11 1
Create a version of your script without Windows line delimiter \r:
tr -d "\r" < sendEmailNotification.sh > sendEmailNotification_fixed.sh
Check and fix line-endings in your script, probably, it was saved with \r\n as line delimiters.
This link may be useful: How to convert DOS/Windows newline (CRLF) to Unix newline (\n) in a Bash script?

bash script automysql sees greater-than character as a table name

CMD="mysqldump -usomeuser -psomepass db_name \> /../SQL_$(date +'%m-%d-%Y').sql"
The above throws the following exception:
mysqldump: Couldn't find table: ">"
"&1>" also is seen as a table name.
I tried the option -all-database(s) and that doesn't work either.
Thanks!
You must not escape greater-than in the command:
CMD="mysqldump -usomeuser -psomepass db_name > /../SQL_$(date +'%m-%d-%Y').sql"
even then, if you try to execute the command just by invoking it, bash treats the greater-than as a positional parameter. You can execute it with a call to sh or bash:
bash -c "$CMD"