¿Cómo ejecutar un comando MySQL desde un script de shell?

131

¿Cómo puedo ejecutar un comando SQL a través de un script de shell para poder automatizarlo?

Quiero restaurar los datos que he recopilado en un archivo SQL utilizando un script de shell. Quiero conectarme a un servidor y restaurar datos. El comando funciona cuando se ejecuta por separado a través de la línea de comando SSH.

Este es el comando que uso:

mysql -h "server-name" -u root "password" "database-name" < "filename.sql"

Este es el código de script de shell que crea el archivo ds_fbids.sqly lo canaliza a mysql.

perl fb_apps_frm_fb.pl
perl fb_new_spider.pl ds_fbids.txt ds_fbids.sql
mysql -h dbservername -u username -ppassword dbname < ds_fbids.sql

¿Cuál es la forma correcta de hacer esto?

MUFC
fuente

Respuestas:

176

Necesita usar la -pbandera para enviar una contraseña. Y es complicado porque no debe haber espacio entre -py la contraseña.

$ mysql -h "server-name" -u "root" "-pXXXXXXXX" "database-name" < "filename.sql"

Si usa un espacio después de -pque el cliente mysql le solicite interactivamente la contraseña, y luego interpreta el siguiente argumento de comando como un nombre de base de datos:

$ mysql -h "server-name" -u "root" -p "XXXXXXXX" "database-name" < "filename.sql"
Enter password: <you type it in here>
ERROR 1049 (42000): Unknown database 'XXXXXXXX'

En realidad, prefiero almacenar el usuario y la contraseña en ~ / .my.cnf para no tener que ponerlo en la línea de comandos:

[client]
user = root
password = XXXXXXXX

Luego:

$ mysql -h "server-name" "database-name" < "filename.sql"

Re tu comentario:

Ejecuto comandos mysql en modo por lotes como el anterior en la línea de comandos y en scripts de shell todo el tiempo. Es difícil diagnosticar lo que está mal con su script de shell, porque no ha compartido el script exacto ni ningún resultado de error. Le sugiero que edite su pregunta original anterior y proporcione ejemplos de lo que sale mal.

Además, cuando soluciono problemas de un script de shell, uso el -xindicador para poder ver cómo se ejecuta cada comando:

$ bash -x myscript.sh
Bill Karwin
fuente
Gracias por una respuesta rápida. Estoy cansado de poner la contraseña en la línea de comando en sí. El verdadero problema es que pongo este comando en el archivo .sh y luego ejecuto este script de shell. El comando en el archivo no se ejecuta en la línea de comando, pero el mismo comando funciona perfectamente bien cuando ejecuto solo el comando en la línea de comando.
MUFC
+ mysql -h dbservername -u user-name -ppassword dbname</br> : No such file or directoryids.sql</br> + $'\r' : command not found2:Este es el mensaje de error que recibí
MUFC
Ok, entonces inferiría que su directorio de trabajo actual no está donde se encuentra el archivo ids.sql. También es posible que haya incrustado nuevas líneas en su secuencia de comandos.
Bill Karwin
Tengo nuevas líneas incrustadas en mi script de shell después de cada comando. Todo lo que contiene mi script de shell son 3 comandos de línea de comandos que no quiero ejecutar por separado, así que creé un script de shell para que se ejecuten sin mi intervención y puse una nueva línea después de cada comando. ¿Eso está causando problemas?
MUFC
Es mejor evitar -psi la contraseña es una cadena nula o vacía, ¿tal vez pueda actualizar su publicación? :)
James Oravec
118

Use esta sintaxis:

mysql -u $user -p$passsword -Bse "command1;command2;....;commandn"
Kshitij Sood
fuente
8
Llegué a esta página desde google y esta es la solución que esperaba (que coincide con el título de la pregunta).
Janaka R Rajapaksha
15
Algunos detalles más sobre las opciones del manual: -B es para lotes, imprime resultados usando la pestaña como separador de columna, con cada fila en una nueva línea. Con esta opción, mysql no usa el archivo de historial. El modo por lotes da como resultado un formato de salida no tabular y el escape de caracteres especiales. -s es modo silencioso. Produce menos producción. -e debe ejecutar la declaración y salir
wranvaud
¡Gracias por tu ayuda! :)
haotang
¿Podría funcionar con un heredoc?
zx1986
1
@ zx1986 Sí y No, a HEREDOC. Depende de cómo quieras usarlo. Usarlo para reemplazar la "command1;command2;....;commandn"parte de esta respuesta no funcionará. Usarlo para reemplazar el uso del archivo redirigido en la sintaxis del OP puede funcionar. He abordado ese problema en mi respuesta a esta pregunta.
Chindraba
45

Todas las respuestas anteriores son geniales. Si es un comando sql simple de una línea que desea ejecutar, también puede usar la opción -e.

mysql -h <host> -u<user> -p<password> database -e \
  "SELECT * FROM blah WHERE foo='bar';"
Jericon
fuente
La consulta entre comillas dobles ("") era lo que necesitaba hacer. Gracias
user3132107
Ya veo, y supongo que ¿debe incluir el punto y coma al final de la consulta?
Lori
19

Cómo ejecutar un script SQL, use esta sintaxis:

mysql --host= localhost --user=root --password=xxxxxx  -e "source dbscript.sql"

Si usa host como localhost, no necesita mencionarlo. Puedes usar esto:

mysql --user=root --password=xxxxxx  -e "source dbscript.sql"

Esto debería funcionar para Windows y Linux.

Si el contenido de la contraseña contiene un !(signo de exclamación), debe agregar una \(barra diagonal inversa) delante de él.

Milinda Bandara
fuente
1
¿Cómo especificar la base de datos? ¿debería estar dentro de -e, como -e "use abc; source dbscript.sql"?
Abdul Muneer
9

El núcleo de la pregunta ya ha sido respondido varias veces, solo pensé agregar que los backticks (`s) se han inclinado tanto en el scripting de shell como en SQL. Si necesita usarlos en SQL para especificar un nombre de tabla o base de datos, deberá escapar de ellos en el script de shell de la siguiente manera:

mysql -p=password -u "root" -Bse "CREATE DATABASE \`${1}_database\`;
CREATE USER '$1'@'%' IDENTIFIED BY '$2';
GRANT ALL PRIVILEGES ON `${1}_database`.* TO '$1'@'%' WITH GRANT OPTION;"

Por supuesto, no se debe generar SQL a través de la entrada de usuario concatenada (argumentos pasados) a menos que confíe en la entrada del usuario. Sería mucho más seguro ponerlo en otro lenguaje de script con soporte para parámetros / escapar correctamente las cadenas para la inserción en MySQL.

Li1t
fuente
5
mysql -h "hostname" -u usr_name -pPASSWD "db_name" < sql_script_file

(use la ruta completa para sql_script_file si es necesario)

Si desea redirigir la salida a un archivo

mysql -h "hostname" -u usr_name -pPASSWD "db_name" < sql_script_file > out_file
vino
fuente
@Gus, en primer lugar gracias por los valiosos comentarios. Funcionó como un encanto para mí. Quiero que la salida sea un archivo Excel o .csv. ¿Cómo puedo lograr eso? Gracias por adelantado.
Ash_and_Perl
@Ash_and_Perl Solo edité esta respuesta, gracias a mí, no a mí, es su respuesta. Si tiene una pregunta propia y ya ha intentado encontrar una solución por su cuenta , le sugiero que cree una pregunta. De esa manera, puede detallar lo que intentó, cómo falló y las personas pueden darle una respuesta completa y completa (¡y obtener puntos por ello!
Gus
5

Olvidó -po --password=(este último es mejor legible):

mysql -h "$server_name" "--user=$user" "--password=$password" "--database=$database_name" < "filename.sql"

(Las comillas son innecesarias si está seguro de que sus credenciales / nombres no contienen espacios o caracteres especiales de shell).

Tenga en cuenta que la página de manual también dice que proporcionar las credenciales en la línea de comandos no es seguro. Así que sigue los consejos de Bill sobre my.cnf.

Orejas puntiagudas
fuente
4

Como se indicó anteriormente, puede usar -p para pasar la contraseña al servidor.

Pero yo recomiendo esto:

mysql -h "hostaddress" -u "username" -p "database-name" < "sqlfile.sql"

Observe que la contraseña no está allí. Luego le pedirá la contraseña. ENTONCES lo escribiría. Para que su contraseña no se registre en el historial de la línea de comandos del servidor.

Esta es una medida de seguridad básica.

Si la seguridad no es una preocupación, simplemente eliminaría temporalmente la contraseña del usuario de la base de datos. Luego, después de la importación, vuelva a agregarlo.

De esta manera, cualquier otra cuenta que pueda tener que comparta la misma contraseña no se verá comprometida.

También parece que en su script de shell no está esperando / verificando si el archivo que está tratando de importar realmente existe. Es posible que el script perl aún no esté terminado.

Sterling Hamilton
fuente
1
Te perdiste la parte "automatizada" de la pregunta y eliminar temporalmente la contraseña es una muy mala idea.
PointedEars
Lo leo como "restaurar" y "automatizado", que significa "automatizado pero no para siempre". Pero como dije "si la seguridad no es una preocupación". Estoy de acuerdo, es una REALMENTE mala idea.
Sterling Hamilton
Lo siento si he creado confusión. Lo que quise decir con Automatizado es que tengo dos scripts perl que se usan para generar el archivo .sql, pero el comando para volcar ese archivo en DB no se ejecuta mediante el script de shell, pero funciona absolutamente si ejecuto ese comando en una línea de comando. Quiero eliminar el esfuerzo de ejecutar ese comando en la línea de comando y ejecutarlo a través del script de shell.
MUFC
1
Vaibav: si pudieras poner el script de shell real dentro de tu pregunta, podría ayudarte más.
Sterling Hamilton
perl fb_apps_frm_fb.pl</br> perl fb_new_spider.pl ds_fbids.txt ds_fbids.sql` </br>mysql -h dbservername -u username -ppassword dbname < ds_fbids.sql
MUFC
3

Utilizar

echo "your sql script;" | mysql -u -p -h db_name
senninha
fuente
3

Para "automatizar" el proceso de importación del .sqlarchivo generado , evitando al mismo tiempo todas las trampas que pueden ocultarse al intentar pasar los archivos stdiny stdout, simplemente dígale a MySQL que ejecute el .sqlarchivo generado utilizando el SOURCEcomando en MySQL.

La sintaxis en la breve pero excelente respuesta , de Kshitij Sood , ofrece el mejor punto de partida. En resumen, modifique el comando del OP de acuerdo con la sintaxis de Kshitij Sood y reemplace los comandos con el SOURCEcomando:

#!/bin/bash
mysql -u$user -p$password $dbname -Bse "SOURCE ds_fbids.sql
SOURCE ds_fbidx.sql"

Si el nombre de la base de datos se incluye en el .sqlarchivo generado , se puede quitar del comando.

La presunción aquí es que el archivo generado es válido como un .sqlarchivo por sí solo. Al no tener el archivo redirigido, canalizado o de cualquier otra manera manejado por el shell, no hay problema con la necesidad de escapar de cualquiera de los caracteres en la salida generada debido al shell. Las reglas con respecto a lo que se debe escapar en un .sqlarchivo, por supuesto, todavía se aplican.

La forma de abordar los problemas de seguridad relacionados con la contraseña en la línea de comandos, o en un my.cnfarchivo, etc., se ha abordado bien en otras respuestas, con algunas sugerencias excelentes. Mi respuesta favorita , de Danny , cubre eso, incluyendo cómo manejar el problema cuando se trata de crontrabajos o cualquier otra cosa.


Para abordar un comentario (pregunta?) Sobre la respuesta corta que mencioné: No, no se puede usar con una sintaxis HEREDOC, ya que se da ese comando de shell. HEREDOC se puede usar en la sintaxis de la versión de redirección (sin la -Bseopción), ya que la redirección de E / S es la base de HEREDOC. Si necesita la funcionalidad de HEREDOC, sería mejor usarlo en la creación de un .sqlarchivo, incluso si es temporal, y usar ese archivo como el "comando" para ejecutar con la línea de lote MySQL.

#!/bin/bash
cat >temp.sql <<SQL_STATEMENTS
...
SELECT \`column_name\` FROM \`table_name\` WHERE \`column_name\`='$shell_variable';
...
SQL_STATEMENTS
mysql -u $user -p$password $db_name -Be "SOURCE temp.sql"
rm -f temp.sql

Tenga en cuenta que debido a la expansión de shell, puede usar variables de shell y de entorno dentro de HEREDOC. El inconveniente es que debes escapar de todos y cada uno de los backticks. MySQL los usa como delimitadores para identificadores, pero el shell, que obtiene la cadena primero, los usa como delimitadores de comandos ejecutables. Echa de menos el escape en un solo backtick de los comandos de MySQL, y todo explota con errores. Todo el problema se puede resolver utilizando un LimitString citado para el HEREDOC:

#!/bin/bash
cat >temp.sql <<'SQL_STATEMENTS'
...
SELECT `column_name` FROM `table_name` WHERE `column_name`='constant_value';
...
SQL_STATEMENTS
mysql -u $user -p$password $db_name -Be "SOURCE temp.sql"
rm -f temp.sql

Eliminar la expansión de shell de esa manera elimina la necesidad de escapar de los backticks y otros caracteres especiales de shell. También elimina la capacidad de usar shell y variables de entorno dentro de él. Para empezar, eso elimina los beneficios de usar un HEREDOC dentro del script de shell.

La otra opción es utilizar las cadenas entre comillas de varias líneas permitidas en Bash con la versión de sintaxis por lotes (con el -Bse). No conozco otros proyectiles, así que no puedo decir si también funcionan allí. Debería usar esto para ejecutar más de un .sqlarchivo con el SOURCEcomando de todos modos, ya que eso no está terminado por un ;como otros comandos MySQL, y solo se permite uno por línea. La cadena de varias líneas puede ser entre comillas simples o dobles, con los efectos normales en la expansión de shell. También tiene las mismas advertencias que el uso de la sintaxis HEREDOC para backticks, etc.

Una solución potencialmente mejor sería usar un lenguaje de script, Perl, Python, etc., para crear el .sqlarchivo, como lo hizo el OP, y SOURCEese archivo usando la sintaxis de comando simple en la parte superior. Los lenguajes de secuencias de comandos son mucho mejores en la manipulación de cadenas que el shell, y la mayoría tienen procedimientos integrados para manejar las citas y los escapes necesarios cuando se trata con MySQL.

Chindraba
fuente
2

Una consideración importante para acceder a mysql desde un script de shell utilizado en cron, es que mysql mira al usuario conectado para determinar un .my.cnf para cargar.

Eso no funciona con cron. También puede ser confuso si está usando su / sudo, ya que el usuario conectado puede no ser el usuario con el que está ejecutando.

Yo uso algo como:

mysql --defaults-extra-file=/path/to/specific/.my.cnf -e 'SELECT something FROM sometable'

Solo asegúrese de que la propiedad y los permisos de usuarios y grupos estén configurados de manera adecuada y estricta en el archivo .my.cnf.

Danny
fuente
1
#!/bin/sh
#Procedures = update
#Scheduled at : Every 00.05 

v_path=/etc/database_jobs
v_cnt=0

MAILTO="[email protected] [email protected] [email protected]"
touch "$v_path/db_db_log.log"

#test
mysql -uusername -ppassword -h111.111.111.111 db_name -e "CALL functionName()" > $v_path/db_db_log.log 2>&1
if [ "$?" -eq 0 ]
  then
   v_cnt=`expr $v_cnt + 1`
  mail -s "db Attendance Update has been run successfully" $MAILTO < $v_path/db_db_log.log
 else
   mail -s "Alert : db Attendance Update has been failed" $MAILTO < $v_path/db_db_log.log
   exit
fi
kartavya soni
fuente
0
mysql_config_editor set --login-path=storedPasswordKey --host=localhost --user=root --password

¿Cómo ejecuto una línea de comando con una contraseña segura? usa el editor de configuración !!!

A partir de mysql 5.6.6 puede almacenar la contraseña en un archivo de configuración y luego ejecutar comandos cli como este ...

mysql --login-path=storedPasswordKey ....

--login-path reemplaza las variables ... host, usuario y contraseña. excelente derecho!

Artistan
fuente
0

He escrito un script de shell que leerá los datos del archivo de propiedades y luego ejecutará el script mysql en el script de shell. compartir esto puede ayudar a otros.

#!/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
flopcoder
fuente