How to escape 3 level in bash (su command, psql, then query) - bash

I'm working on a bash script used to install an application on a server and create a postgresql user for the application.
The matter is that if the user contains a special char, it's not escaped. So I would like to escape the name in the query.
Here is a part of my code:
db_user="test-test"
db_password=1234
su - postgres -c "psql -c \"CREATE USER $db_user WITH PASSWORD '$db_password';\""
So I have to escape:
the command for su -c ...
the query for psql -c ...
the user name in $db_user
the password in $db_password
Of course, if I add quotes around $db_user it's not working anymore. I tried others solutions, but none working.
Any idea?
Thanks

Let the shell do the work for you for you. In this case, that means printf '%q' -- which returns an eval-safe version of each literal argument.
cmd=( psql -c "CREATE USER $db_user WITH PASSWORD '$db_password';" )
printf -v cmd_q '%q ' "${cmd[#]}"
su - postgres -c "$cmd_q"
By the way, substituting literal text into SQL queries this way is massively insecure -- but if you follow the process above you're prone to only SQL injection bugs, not shell injection bugs.
To avoid SQL injection bugs as well, you want to avoid textual substitution of values into your queries at all.
# This is inefficient, but makes it completely unambiguous that our heredocs are literal
query=$(cat <<'EOF'
CREATE USER :new_user_name WITH PASSWORD :'new_user_password';
EOF
)
cmd=(
psql \
--set=new_user_name="$db_user" \
--set=new_user_password="$db_password"
-c "$query"
)
printf -v cmd_q '%q ' "${cmd[#]}"
su - postgres -c "$cmd_q"
See http://caryrobbins.com/dev/postgres-scripting/ for more on this technique. The classic XKCD linked in its header (also useful as a concrete example of the risks the original code was taking!) has its original source at https://xkcd.com/327/.

Related

Copy output of sql-query to a file

I want to export a random entry of my database into a file with the command
SELECT * FROM my_table ORDER BY RANDOM() LIMIT 1 \g /path/file;
This query works if I enter it in my db terminal, but I want to us this query with a bash script but then I get the error: syntax error at or near "\g"
My bash script looks like this:
PGPASSWORD=*** psql -U user -d db_name -h localhost -p port -t -c "SELECT * FROM my_table ORDER BY RANDOM() LIMIT 1 \g /path/file"
Bash is interpreting the string and trying to interpolate it. Probably, escaping the backslash will solve your problem.
PGPASSWORD=*** psql -U user -d db_name -h localhost -p port -t -c "SELECT * FROM my_table ORDER BY RANDOM() LIMIT 1 \\g /path/file"
A SQL statement terminated by \g is not supported by the -c command switch. Per documentation of -c:
-c command
...
command must be either a command string that is completely parsable by the server (i.e., it contains no psql-specific features), or a single backslash command. Thus you cannot mix SQL
and psql meta-commands with this option
To redirect the results to a file, there are several options:
shell redirection: psql [other options] -Atc 'SELECT...' >/path/to/data.txt
-A is to switch to unaligned mode (no space fillers to align columns).
put the SQL part in a heredoc text instead of the command line:
psql [options] <<EOF
SELECT ... \g /path/to/file
EOF
This form has the advantage that multiline statements or multiple statements are supported directly.
\copy of the query. Be aware that COPY to a FILE is different: it creates the file on the server with the permissions of postgres and requires being a database superuser. COPY TO STDOUT works too but is not better than SELECT concerning the redirection.
I found a solution for my script, and now it works.
#!/bin/bash
RANDOM_NUMBER=0
while true
do
for i in `seq 1`
do
RANDOM_NUMBER=$(($RANDOM % 100000))
echo $RANDOM_NUMBER
PGPASSWORD=*** psql -U user_name -d db_name -h localhost -p PORT -c
"INSERT INTO numbers (number) VALUES ('$RANDOM_NUMBER');"
done
sleep 10
for i in `seq 1`
do
PGPASSWORD=*** psql -U user_name -d db_name -h localhost -p PORT -c
"DELETE FROM numbers WHERE id = (SELECT id FROM numbers ORDER BY RANDOM() LIMIT 1);"
done
done

How to drop a mysql database from a script using ~/.my.cnf?

I need to drop a mysql database directlly from a script.
I create the file ~/.my.cnf and chmod 600.
[client]
user = "**************"
password = "*********"
safe-updates
My script includes this :
CMD4="echo 'DROP DATABASE db_test;' | mysql"
curl -fs -- "$URL" | grep -q -- "$WORD1" && $CMD4
I can´t execute the command I just get this output:
'DROP DATABASE db_test;' | mysql
The database is not dropped.
What´s wrong with it?
Thanks
I think you should enclose the below statement in Grave accent (``) instead of double quotes ("") where you are initializing the value of CMD4 on the both
echo 'DROP DATABASE db_test;' | mysql
You can eliminate the pipe using mysql -Bse 'expression'. So in your case, with the correct ~/.my.cnf setting you can simply do:
mysql -Bse "drop database db_test"
You also have the full range of mysql options as well. i.e. mysql -h somehost -u someuser -p pass -Bse "expression"
Please, don't use variable to store code. Use function instead:
function dropdb {
echo 'DROP DATABASE db_test;' | mysql # aren't you missing some arguments here?
# like `-u username -p db`
}
curl -fs -- "$URL" | grep -q -- "$WORD1" && dropdb

bash script returns error "ERROR: syntax error at end of input LINE 1: SELECT" for psql request to copy the table to an external file

What should I do for making it work?
#!/bin/bash
TABLENAMES="user_stats"
ssh -t railsapps#xxx.xxx.xxx.xx -p xxx bash -c "'
for TABLENAME in $TABLENAMES
do
psql -d mydb -P format=unaligned -P tuples_only -P fieldsep=\, -c "SELECT * FROM $TABLENAME" > /tmp/$TABLENAME
done
'"
General problem: how to periodically dump the database tables to a local machine from a psql database in a single bash script run on Mac OS X?
Firstly, you should test your SQL and bash scripts remotely (do SSH interactively).
I think your problem is caused by a bad mix of quote / double-quote. I think the star (*) and $TABLENAME are expensed before the SSH call, so too early. Try to put a backslash before the $ sign.
You should use the verbose or the debug option, to help to understand what is really executed:
ssh -t railsapps#xxx.xxx.xxx.xx -p xxx bash -vxc "'
for TABLENAME in \$TABLENAMES; do
psql -d mydb -P format=unaligned -P tuples_only -P fieldsep=\, -c "SELECT \* FROM \$TABLENAME" > /tmp/\$TABLENAME
done
'"

insert bash-variables in psql-command

I'm going crazy while trying to insert bash-variables in a psql commands as connection paramters as well as variables in the command itself. The following example works properly:
psql -U postgres -h localhost -p 5432 -c "CREATE DATABASE testdb WITH ENCODING='UTF8' OWNER=postgres TABLESPACE=pg_default TEMPLATE=template_postgis CONNECTION LIMIT=-1;"
Now I'm trying to exchange each parameter through a variable, which is held in special config-file.
Non-working example:
dbserver=localhost
dbport=5432
dbowner=postgres
dbname=testdb
dbtemplate=template_postgis
dbtablespace=pg_default
psql -U '$dbowner' -h '$dbserver' -p '$dbport' -c "CREATE DATABASE '$dbname' WITH ENCODING='UTF8' OWNER='§dbowner' TABLESPACE='$dbtablespace' TEMPLATE='$dbtemplate'
CONNECTION LIMIT=-1;"
I've tried several quotings, backquotes and escape-slashes already but smhow it still won't work.
Thanks in advance, knutella
Use double quotes ("). Single quotes (') does not interpret shell variables inside.
Try it
echo '$USER' "$USER"
See man bash.
This works... most of the quotes are not needed:
psql -U $dbowner -h $dbserver -p $dbport -c "CREATE DATABASE $dbname WITH ENCODING='UTF8' OWNER=$dbowner TABLESPACE=$dbtablespace TEMPLATE=$dbtemplate CONNECTION LIMIT=-1;"

Prompting for MySQLDump password in a bash script?

I'm trying to write a bash script that runs a mysqldump command that uses the -p flag. This flag prompts the user for a password, which works as expected when run in the shell directly, but does not appear when run in a script.
#!/usr/bin/env
ssh user#domain.com 'mysqldump -u mysqluser -p --databases foo | bzip2' > ~/temp/foo-dump.sql.bz2
Now I could embed the password in the script or pass it as an arguments, but I really want the script to prompt the user for the password so the password doesn't show up in my scripts repo or in my bash history.
Anyone have any idea on how to accomplish this?
This should do the trick:
read -p "mysql password: " PASS && ssh user#domain.com 'mysqldump -u mysqluser -p'$PASS' --databases foo | bzip2' > foo-dump.sql.bz2 ; PASS=""
In this case, you will first enter the mysql password, and then be prompted for the ssh password. Note that the mysql password will not be hidden, i.e., someone can read it over your shoulder. If you want to avoid that, use the flag -s
read -s -p "mysql password: " PASS && ...
Note also that there mustn't be any space between the "p" (in -p for password) and the quotation mark for the password variable.
Also, your shebang is not specifying which interpreter to use, which might be a problem. I'd suggest you use #!/bin/bash or #!/usr/bin/env bash.

Resources