echo variable result in quotes BASH - bash

I have a bash script like this:
#!/bin/bash
pavadinimas=$1
pavadinimas2=${pavadinimas::-4}
echo "#!/bin/sh
mysql -uUSER -pPASSWORD -DDatabase -e 'UPDATE boom SET count = count + 1 WHERE Failo_vardas="$pavadinimas"';
vlc -f --play-and-exit /var/www/html/uploads/$pavadinimas" > /var/www/html/script/"$pavadinimas2.sh"
And I'm having problem with this line:
mysql -uUSER -pPASSWORD -DDatabase -e 'UPDATE boom SET count = count + 1 WHERE Failo_vardas="$pavadinimas"';
As you see I want to add the variable to quotes, but It comes out without It. I tried a lot of combinations to solve this out, but I failed. Lack of experience :/
Script result:
#!/bin/sh
mysql -uUSER -pPASSWORD -DDatabase -e 'UPDATE boom SET count = parodymai + 1 WHERE Failo_vardas=name.mp4';
vlc -f --play-and-exit /var/www/html/uploads/gaidys.mp4
I want to echo the variable in quotes like this:
mysql -uUSER -pPASSWORD -DDatabase -e 'UPDATE boom SET count = count + 1 WHERE Failo_vardas="name.mp4"';

You are really close. You just have to escape the quotes that you want to use.
e.g. WHERE Failo_vardas=\"$pavadinimas\"

You have to leave the single quoting or your variable won't be evaluated.
So insert a single quote after the double quote, put your variable to evaluate, and re-insert a quote after your variable. Where the single-quoting ends, your env. variable will be evaluated instead of being treated literally.
Demo:
$ pavadinimas=name.mp4
$ echo 'UPDATE boom SET count = count + 1 WHERE Failo_vardas="'$pavadinimas'"';
result:
UPDATE boom SET count = count + 1 WHERE Failo_vardas="name.mp4"

Related

Check if a role exists in PostgreSQL using psql

I need in a bash script a IF condition on the existence of a role in a PostgreSQL database. I have found solutions in SQL code [1, 2], but I need something I can use directly in bash, I assume with the help of psql. In [2] there are also psql solutions, but I don't manage to adapt it in a IF statement.
I have tried this unsuccessfully (I am a PostgreSQL and bash newbie):
psql_USER=my
if [ "$( psql -h db -U postgres --no-psqlrc --single-transaction --pset=pager=off --tuples-only --set=ON_ERROR_STOP=1 -tc "SELECT 1 FROM pg_user WHERE usename = $psql_USER" | grep -q 1 )" == '1' ] > /dev/null 2> /dev/null; then
echo "HOURRA !"
fi;
Result is:
Password for user postgres:
ERROR: column « my » does not exist
LINE 1: SELECT 1 FROM pg_user WHERE usename = my
^
I would avoid the quoting problem like this:
if psql -Atq -c "SELECT '#' || usename || '#' FROM pg_user" | grep -q '#'"$psql_USER"'#'
then
echo yes
fi
The psql invocation selects a list of all usernames, prefixed and suffixed with #. The grep has return code 0 if psql_USER contains one of these user names, else 1. The then branch of if is only taken if the return code of the pipeline is 0, that is, if the user exists in the database.

Script with multiline string and for loop

Writing a quick script for a temporary/repetitive task. Wrote a basic solution that works:
for thing in "$#";
do
/usr/mysql/bin/mysql -u xyz -p pdq <<END;
UPDATE table
SET table_atr = 'NW'
WHERE record_id = $thing
END
done
This works but forces a password check for every member of argument array (not ideal).
Tried to update it to this:
if {$# -le 1}; then
for thing in "$#";
do
/usr/mysql/bin/mysql -u xyz -p pdq <<END;
UPDATE table
SET table_atr = 'NW'
WHERE record_id = $thing
END
done
else
things = ""
for thing in "$#";
do
things += "$thing"
if {$thing == $#[$# - 1]}; then
things += "\n"
continue
else
things += ",\n"
done
/usr/mysql/bin/mysql -u xyz -p pdq <<END;
UPDATE table
SET table_atr = 'NW'
WHERE record_id IN
(
$things
)
END
TLDR: If there is more than one argument: do a for loop to fill a WHERE .. IN () statement. I realize this doesn't even need to be a multiline string and maybe that is my issue but the error I'm getting is (apparently) unrealted .
The error I get is:
line 24: syntax error near unexpected token' done'
line 24: ' done'
Neither I, nor my supervisor have much experience with shell scripts but I cannot see any syntax error with the 2nd for loop. Its exactly the same as the first which executes fine.
Any help is greatly appreciated, I may just have to go to the basic version or write this as a Perl script instead. Thanks!
Thanks everyone for all the advice. This was an edit that worked:
#!/bin/sh
if [ $# -le 1 ]; then
for thing in "$#";
do
/usr/mysql/bin/mysql -u xyz -p pdq <<END;
UPDATE table
SET table_atr = 'NW'
WHERE record_id = $thing
END
done
else
things=""
i=1
for thing in "$#";
do
things+="$thing"
if [ $i -eq $# ]; then
things+=""
else
things+=", "
fi
((i+=1))
done
/usr/mysql/bin/mysql -u xyz -p pdq <<END;
UPDATE table
SET table_atr = 'NW'
WHERE record_id IN ($things)
END
fi
There were indeed many syntax errors and changing the WHERE .. IN () string construction to a single line made this a lot easier. Luckily I didn't have to worry about inserting single quotes, mysql took the query without them.
I came out of this with a much higher respect for bash scripting. It is a serious language that requires its own study and I will approach it with much more attention to detail in the future.
Thanks again.

How can I disable * expansion in a script?

I have a strange problem - possibly I'm just going blind. I have this short script, which replaces the string #qry# in the here-document with a select statement in a file and then pipes it to mysql:
#!/bin/bash
if [[ "$1" == "-h" ]]
then
echo "sqljob [sqlfile] [procnm] [host] [database] [config file]"
echo " sqlfile: text file containing an SQL statement"
echo " procnm: name that will given to the new, stored procedure"
echo " host: hostname of IP address of the database server"
echo " database: the procedure will be created here"
echo " config file: default configuration file with username and password"
exit
fi
infile=$1
procnm=$2
hn=$3
pn=$4
db=$5
mycfg=$6
{
set -o noglob
sed -e "s/#qry#/$(echo $(cat $infile))/g" <<!
drop procedure if exists $procnm;
delete from jobs where jobname="$procnm";
insert into jobs
set
notes="SQL job $procnm",
jobname="$procnm",
parm_tmpl='int';
delimiter //
create procedure $procnm(vqid int)
begin
call joblogmsg(vqid,0,"$procnm","","Executing #qry#");
drop table if exists ${procnm}_res;
create table ${procnm}_res as
#qry#
end//
delimiter ;
!
} | mysql --defaults-file=$mycfg -h $hn -P $pn $db
However, when the select contains *, it expands to whatever is in the directory even though I use noglob. However, it works from the command line:
$ set -o noglob
$ ls *
What am I doing wrong?
Edit
Block Comments in a Shell Script has been suggested as a duplicate, but as you will notice, I need to expand ${procnm} in the here-doc; I just need to avoid the same happening to select *.
I suspect it is because the construct echo (cat). The echo command gets the * from the cat command and the shell in which it runs expands it. In that shell set noglob is not active.
Try leaving the echo away: /$(cat $infile)/, in the end that is the data you need; then there is no extra glob expansion by a shell.

How to make read -d return true

I am using the following construct to set a multiline string in bash shell. But this always returns false which does not work when I set set -e. How can I make this to return success?
#!/bin/bash
set -x
set -e
read -d '' QUERY <<EOF
UPDATE table_name SET
field1 = 'value',
field2 = 'value'
WHERE id = 1;
EOF
mysql table_name -e "$QUERY"
While not an answer to your original question, this does get your problem solved. Consider using substitution and e.g. cat
QUERY=$(cat <<EOM
test
test2
EOM
)
mysql table_name -e "$QUERY"
Please, be aware of safety issues if you're reading those values from unsanitized input.

Bash script - cycle through MySQL records and update record based on user select

I'm a bit stuck, I need to loop through all of my mysql records and update one of the fields based on user input.
I was going to use the following to do the loop:
mysql -uuser -ppassword -s -e "SELECT company,product,category FROM MyTable"|while read varcomp varprod varcat;do
..and then use a PS3 select to provide the option:
PS3 "Please select new category: "
select opt1 in "Blocks" "Dolls" "Puzzles"
...before using, the following to update:
mysql -uuser -ppassword -s -e "UPDATE MyTable SET Category='$opt1' WHERE company='$varcomp' AND product='$varprod'"
...and then closing the loop.
I just can't seem to get all of the components to work together. I think it's because I'm using a while loop? Any advice on the best way to do this would be appreciated...
This should work:
while read -u 4 varcomp varprod varcat; do
echo "Company: $varcomp - Product: $varprod - Category: $varcat"
PS3="Please select new category: "
select opt in "Blocks" "Dolls" "Puzzles"; do
mysql -uuser -ppassword -s -e "UPDATE MyTable SET Category='$opt' WHERE company='$varcomp' AND product='$varprod';"
break
done
done 4< <(mysql -uuser -ppassword -s --skip-column-names -e "SELECT company,product,category FROM MyTable;")
You had the right idea, but the issue with using select inside a while read loop is that select and read both read input from stdin, so select would read all the SQL lines instead of prompting for user input. Instead, we redirect the mysql results to file descriptor 4, then tell read to use that instead of stdin, while select still reads from stdin as usual.
Note that if a user puts in a different value for select (eg 8 instead of 1, 2 or 3) then $opt will be null. You could replace the lines inside the select with:
[[ -n "$opt" ]] && mysql -uuser -ppassword -s -e "UPDATE MyTable SET Category='$opt' WHERE company='$varcomp' AND product='$varprod';" && break
This will keep prompting the user for input until they give a valid response.

Resources