Execute bash command in batch with mysql results - mysql

I maintain several servers having the same web app through bash scripts. Commands include basically ssh and scp, and they are the same for every server, just the IP, the user and the port are different between servers. So far I wrote as many commands as servers to maintain in same script. This works well but as I start to have many servers to maintain, I would prefer to list these servers in a MySQL table, and then use this list in my bash scripts, thus I I would not need to keep all of them updated when I have new servers to maintain.
Currently I have problems to extract data from MySQL in a proper way that could be then executed in bash script.
My current approach is to use CONCAT function in the query as follow:
outputofquery=$($MYSQL -u"$USER" --batch -p"$PASSWORD" --skip-column-names -h"$HOST" -e "SELECT CONCAT('scp -P ', server_port, ' $file ', server_user, '#', server_ip, ':/var/www/html/site/') FROM server_ssh;" $DBNAME)
echo ${outputofquery%$'\t;'*}
Giving the following result:
scp -P 22 text.php user1#1.1.1.1:/var/www/html/site/ scp -P 12345 text.php user2#2.2.2.2:/var/www/html/site/
Every command resulting from the MySQL query is put on the same line, meaning that this cannot be executed..
I though to add a semicolon just after site/ in the query, so that even if every command is on the same line, they could be executed independently but it happens that only the last scp command gets executed and those before are ignored.
Could you please let me know how I could execute ssh and scp commands in batch with data coming from a MySQL table?

Finally I succeed to execute batch commands with MySQL data. I build the commands to execute with concatenate function, depending on the line number extracted from MySQL. Below is my script
#!/bin/sh
HOST="localhost"
USER="root"
DBNAME="mydb"
PASSWORD="mypassord"
MYSQL="/Applications/XAMPP/bin/mysql"
file='text.php'
i=0;
command='';
ips_and_ports=$($MYSQL -u"$USER" -p"$PASSWORD" -h"$HOST" -BNe "SELECT server_port,server_user,server_ip FROM server_ssh;" $DBNAME)
for line in $ips_and_ports
do
(( i++ )); #increment the line number
if [ $(($i % 3)) -eq 1 ] #check if it is the first element of a line extracted from MySQL (server_port)
then
command="scp -P $line"; #initialize a new command to execute
elif [ $(($i % 3)) -eq 2 ] #check if it is the second element of a line extracted from MySQL (server_user)
then
command+=" $file $line#"; #concatenate
elif [ $(($i % 3)) -eq 0 ] #check if it is the third element of a line extracted from MySQL (server_ip)
then
command+="$line:/var/www/html/my_web_app/;"; #concatenate
eval $command; #execute the command
fi
done;

I can't put a comment because I'm new.
Have you tried to put a \n or && at the end of your mysql concatenation ?
You can also change the approach and assign the server and the port to variables and loop over these variables to execute the ssh and the scp command. For example
ips_and_ports=$($MYSQL -u"$USER" --batch -p"$PASSWORD" --skip-column-names -h"$HOST" -e "SELECT server_port, server_ip FROM extranet.server_ssh;" $DBNAME)
for $line in $ips_and_ports
do
OLDIFS=$IFS;
IFS=" "; # set the separator to the tab character (PS: this is 4 spaces, change accordingly )
for $el in $line
do
set -- $el; # separate each line by tab and assign the variables to $1, $2 ..
port=$1;
ip=$2;
scp -P 22 text.php user1#$ip:/var/www/html/site/ scp -P $port
done;
IFS=$OLDIFS; # put the separator back to it's original value
done;
I haven't tested the code but I hope you got my idea !
Good luck
more infos about the IFS and OLDIFS in link

Related

Loop, array, mysql, Bash, Linux

I want to write a script that sends the select to the database
data_path="https://localhost/public/"
data_name=(PXL_20220628_152928222.mp4 PXL_20220628_163301667.mp4)
for q in "${data_name[#]}";
do
data_l=$(sudo mysql -h localhost -P 3306 -u ck****** -p"*******" -e "SELECT iddata FROM
ck*****.data WHERE links = '$data_path$q'")
done
echo "$data_l"
In response I get only one record (id first file PXL_20220628_152928222.mp4 )
The loop is not working
I copied your script to a file with a shebang, added an echo in front of the sudo to show the command being executed and moved the one AFTER the loop inside to show the result.
#! /bin/bash
data_path="https://localhost/public/"
data_name=(PXL_20220628_152928222.mp4 PXL_20220628_163301667.mp4)
for q in "${data_name[#]}"; do
# data_l=$(sudo mysql -h localhost -P 3306 -u ck****** -p"*******" -e " SELECT iddata FROM ck*****.data WHERE links = '$data_path$q'
data_l=$(echo "$q")
echo "$data_l" # this was outside the loop
done
# echo "$data_l" # not here, this only reports the LAST value
Running it:
PXL_20220628_152928222.mp4
PXL_20220628_163301667.mp4
You were assigning it, but not reporting it, then overwriting with the second assignment, and only reporting that one after the loop was done.
Just move the echo at the end inside the loop.

connect to mysql db and execute query and export result to variable - bash script

I want to connect to mysql databse and execute some queries and export its result to a varibale, and do all of these need to be done entirely by bash script
I have a snippet code but does not work.
#!/bin/bash
BASEDIR=$(dirname $0)
cd $BASEDIR
mysqlUser=n_userdb
mysqlPass=d2FVR0NA3
mysqlDb=n_datadb
result=$(mysql -u $mysqlUser -p$mysqlPass -D $mysqlDb -e "select * from confs limit 1")
echo "${result}" >> a.txt
whats the problem ?
The issue was resolved in the chat by using the correct password.
If you further want to get only the data, use mysql with -NB (or --skip-column-names and --batch).
Also, the script needs to quote the variable expansions, or there will be issues with usernames/passwords containing characters that are special to the shell. Additionally, uppercase variable names are usually reserved for system variables.
#!/bin/sh
basedir=$(dirname "$0")
mysqlUser='n_userdb'
mysqlPass='d2FVR0NA3'
mysqlDb='n_datadb'
cd "$basedir" &&
mysql -NB -u "$mysqlUser" -p"$mysqlPass" -D "$mysqlDb" \
-e 'select * from confs limit 1' >a.txt 2>a-err.txt
Ideally though, you'd use a my.cnf file to configure the username and password.
See e.g.
MySQL Utilities - ~/.my.cnf option file
mysql .my.cnf not reading credentials properly?
Do this:
result=$(mysql -u $mysqlUser -p$mysqlPass -D $mysqlDb -e "select * from confs limit 1" | grep '^\|' | tail -1)
The $() statement of Bash has trouble handling variables which contain multiple lines so the above hack greps only the interesting part: the data

way to connect mysql which executes only once inside the for loop in bash script?

I have a script which will execute Insert Query n times - for that i have used FOR loop , but the problem is the command which connects to remote mysql also executes n times. Here is the script for the better idea for my problem.
#!/bin/bash -X
#fields: id|alias|booking_time|contact_no|deleted|grace|number_in_queue|pax|seated_time|status|walk_in_time|queue_id|user_id
echo "Bash version ${BASH_VERSION}..."
for i in {1..5..1}
do
_alias="Name$i"
_contact_no=$(cat /dev/urandom | tr -dc '1-9' | fold -w 10 | head -n 1)
_deleted="FALSE"
_number_in_queue=$i
_pax=$(( $RANDOM % 10 + 20 ))
_status="waiting"
_queue_id=424
_user_id=550
mysql -u root -p restbucks << EOF #Want this to execute only One time
INSERT INTO queue_item VALUES ('','$_alias',now(),'$_contact_no','$_deleted',NULL,'$_number_in_queue','$_pax',now(),'$_status',now(),'$_queue_id','$_user_id');
EOF
done
Everytime i try to run the script , it will ask me for the password. What i want is that only once the connection made.
You can move the mysql connect before the for loop.
mysql -u root -p restbucks << EOF #this execute only One time
for i in {1..5..1}
do
.....
done
EOF
Also it is recommended that you can write your queries into a file and then finally execute the file using single connection.
You can refer bulk-mysql-query
That is because it is placed inside the for loop. Every time the control passes to the loop it gets executed. You don't need to connect to your server once you are connected. Try placing the statement before the FOR statement.

bash scripting variable allocation when using here-document

I am new to bash /mysql however I have found tons of help reading threw examples and other people's problems ...but have ran into one of my own .
I am attempting to insert a row into an MySQL database table every time a file is added to a specific directory (for this i am using inotifywait ) anyways here is my bash script
#!/bin/bash
while true; do
filename= "false"
filename= inotifywait --format "%f" -e create /var/www/media2net/torrent
date_field= date +"%F":"%T"
mysql --host=localhost --user=root --password=admin Media2net << EOF
insert into video (title, description, url_video, upload_date)
values('testing','default_description','$filename', '$date_feild');
EOF
echo $filename
done
From this I have verified with echo the variable $filename is properly held at end of bash script however when i look at entry in the table the column url_video has it's default value and not the string represented by $filename
From what i can conclude the variable $filename does not get passed through EOF
i have tried as indicate here http://www.dba-oracle.com/t_access_mysql_with_bash_shell_script.htm
as well as this
Using shell script to insert data into remote MYSQL database
any help of where i can find how to pass variable into query would be greatly appreciated
In your example, filename is set to the empty string (mind the spaces after the = sign!). You need
filename=$(inotifywait --format "%f" -e create /var/www/media2net/torrent)
Similarly,
date_field=$(date +"%F:%T")
and be careful, you have a typo in your mysql command (date_field and note date_feild):
mysql --host=localhost --user=root --password=admin Media2net <<EOF
insert into video (title, description, url_video, upload_date)
values('testing','default_description','$filename', '$date_field');
EOF
Now I hope that you're controlling the filenames. Imagine a filename that contains a single quote e.g., hello'howdy. You'll have a problem in your query. Worse, an evil user who puts a file named whatever','something'); evil_mysql_command; whatever, you'll have the evil command performed! One possibility is to sanitize the filename using printf thus:
printf -v filename '%q' "$(inotifywait --format "%f" -e create /var/www/media2net/torrent)"
This will at least escape the single quotes that could appear in a filename. See Gordon Davisson's comment: the printf trick will not prevent from all the possible attacks, so I really hope you absolutely control the name of the files!
All these suggestions yield the following script:
#!/bin/bash
while true; do
printf -v filename '%q' "$(inotifywait --format "%f" -e create /var/www/media2net/torrent)"
date_field=$(date +"%F:%T")
mysql --host=localhost --user=root --password=admin Media2net <<EOF
insert into video (title, description, url_video, upload_date)
values('testing','default_description','$filename', '$date_field');
EOF
echo "$filename"
done
Edit.
To answer your question in the comment:
why did the script properly echo $filename to my terminal but not send it properly to MySQL, does that have to do with string starting with a space? or something else completely?
That's because when you do something like:
whatever= command
then the variable whatever is set to the empty string, and the command command is executed (with the whatever variable set as environment variable). E.g.,
$ IFS='c' read a b c <<< "AcBcC"
$ echo "$a $b $c"
A B C
$ echo $IFS
$
In your script, the variable filename was in fact never globally set. You can check it by doing this:
$ filename= "false"
$ echo "$filename"
$
What happens is that the environment variable filename is set to empty string then the command false (which happens to exist) is launched using that environment variable and we're done.
When you do this:
filename= inotifywait --format "%f" -e create /var/www/media2net/torrent
the variable filename is set to the empty string, and then the command inotifywait ... is executed with filename as an environment variable (but inotifywait doesn't really care about it). And that's what you saw on your terminal! it was the output of this command. Then you probably saw an empty line, that was the output of
echo $filename
which was equivalent to
echo
since the variable filename expanded to an empty string.
Hope this helps.

How to insert over a million records into a MySQL database?

I have a sql file which contains over a million insert statements. The official tool for MySQL administration is not able to open it because of its size. Is it possible to insert records using a BASH script?
I tried this so far but it doesn't work.
while read line
do
mysql -u root --password=root << eof
use dip;
$line;
eof
done < $1
mysql -u root --password=root <mysqlfile.sql
Try this:
while read line
do
mysql -u root --password=root -c "$line"
done < $1
Notes:
If the sql contains double quotes ("), yo'll have to escape them
If the SQL statements go over multiple lines, you'll have to figure that out
The advantage of this method is each line gets its own transaction, whereas if you fire the whole file in, it could blow the logs being such a large change set