# WP-CLI Bash Scripts > WP SysAdmin --- ## Páginas - [Instalar WP-CLI](https://www.wp-cli.es/instalar/): WP-CLI es una herramienta que complementa WordPress y que permite la ejecución de prácticamente todo lo que se puede hacer... - [Envía tu código](https://www.wp-cli.es/enviar/): Si quieres enviar tu código o conoces algún código bash de WP-CLI por la red, por favor, háznoslo llegar y... --- ## Entradas - [Depuración de consultas a la base de datos](https://www.wp-cli.es/performance/depuracion-consultas-base-datos/): Encontrar los cuellos de botella de la base de datos en WordPress puede llegar a complicarse sin querer. Y aunque... - [Actualizar todos los Salt-Keys](https://www.wp-cli.es/seguridad/actualizar-salt-keys/): WordPress incluye, por seguridad, una serie de claves únicas y aleatorias para mejorar la seguridad de acceso al sistema, las... - [Cron para WordPress MultiSite](https://www.wp-cli.es/multisite/cron-wordpress-multisite/): Cuando decidimos ejecutar los crones en WordPress, hemos de añadir habitualmente una línea por cada uno de los sitios. En... - [Autocompletar para WP-CLI](https://www.wp-cli.es/wp-cli/autocompletar/): Cuando se trabaja en bash, una de las herramientas más habituales es la de usar el tabulador para autocompletar comandos.... - [Ejecutar WP-CLI siempre como root](https://www.wp-cli.es/wp-cli/ejecutar-como-root/): Aunque no está recomendado, en algunos entornos es posible que se trabaje como root y que sea necesario lanzar WP-CLI.... - [Encontrar WordPress y actualizaciones pendientes](https://www.wp-cli.es/wordpress/encontrar-actualizaciones/): ¿Sabes donde están los WordPress en tu servidor? ¿Y si alguno tiene actualizaciones pendientes? Con este bash podrás indicar la... --- # # Detailed Content ## Páginas WP-CLI es una herramienta que complementa WordPress y que permite la ejecución de prácticamente todo lo que se puede hacer con WordPress desde la CLI / Command-line interface (interfaz de línea de comandos). Lo primero que haremos es descargar el fichero (con curl o wget): cd ~ curl -O https://raw. githubusercontent. com/wp-cli/builds/gh-pages/phar/wp-cli. phar Validaremos que funciona correctamente: php wp-cli. phar --info Si todo ha ido correctamente, lo moveremos al PATH para que sea fácilmente ejecutable. chmod +x wp-cli. phar sudo mv wp-cli. phar /usr/local/bin/wp A partir de este momento, cualquier usuario debería poder ejecutar comandos de WP-CLI en cualquier lugar de todo el sistema. Y, para comprobarlo, puedes ir a cualquier lugar y ejecutar lo siguiente: wp --info Que te debe devolver un mensaje similar a este: OS: Linux 5. 4. 0-80-generic #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021 x86_64 Shell: /bin/bash PHP binary: /usr/bin/php8. 0 PHP version: 8. 0. 8 php. ini used: /etc/php/8. 0/cli/php. ini MySQL binary: /usr/bin/mysql MySQL version: mysql Ver 15. 1 Distrib 10. 5. 11-MariaDB, for debian-linux-gnu (x86_64) using readline 5. 2 SQL modes: STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION WP-CLI root dir: phar://wp-cli. phar/vendor/wp-cli/wp-cli WP-CLI vendor dir: phar://wp-cli. phar/vendor WP_CLI phar path: /etc/letsencrypt/live/www. wpsysadmin. com WP-CLI packages dir: /root/. wp-cli/packages/ WP-CLI global config: WP-CLI project config: WP-CLI version: 2. 5. 0 --- Si quieres enviar tu código o conoces algún código bash de WP-CLI por la red, por favor, háznoslo llegar y lo publicaremos. NOTA: Puedes enviar el código o un enlace a él. Además, te pediremos información y enlaces para poner una ficha al final de código. --- --- ## Entradas Encontrar los cuellos de botella de la base de datos en WordPress puede llegar a complicarse sin querer. Y aunque tenemos el Query Monitor, sin duda no siempre es fácil acceder al problema. Para usar Query Debug necesitaremos instalar una extensión. wp package install runcommand/query-debug A partir de aquí, sólo hemos de indicar la URL a analizar y listar sus consultas: wp query-debug --url=https://example. com/2021/08/01/hello-world/ --format=table --path=/webs/example. com/ Esto nos devolverá algo tal que así: +----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+ | seconds | backtrace | query | +----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+ | 0. 000137 | WP_CLI\Runner->load_wordpress, require('wp-settings. php'), include_once('/plugins/heal | SELECT VERSION | | | th-check/health-check. php'), require_once('/plugins/health-check/includes/class-health | | | | -check-site-status. php'), Health_Check_Site_Status->__construct, Health_Check_Site_Sta | | | | tus->init, Health_Check_Site_Status->prepare_sql_data | | | 0. 000433 | wp, WP->main, WP->query_posts, WP_Query->query, WP_Query->get_posts | SELECT SQL_CALC_FOUND_ROWS wpe21jz2_posts. ID FROM wpe21jz2_posts WHERE 1=1 AND wpe2 | | | | 1jz2_posts. post_type = 'post' AND (wpe21jz2_posts. post_status = 'publish') ORDER BY w | | | | pe21jz2_posts. post_date DESC LIMIT 0, 100 | | 4. 8E-5 | wp, WP->main, WP->query_posts, WP_Query->query, WP_Query->get_posts, WP_Query->set_fou | SELECT FOUND_ROWS | | | nd_posts | | | 0. 000214 | require_once('wp-includes/template-loader. php'), get_front_page_template, get_query_te | SELECT wpe21jz2_posts. * FROM wpe21jz2_posts WHERE 1=1 AND wpe21jz2_posts. post_name | | | mplate, locate_block_template, resolve_block_template, get_block_templates, WP_Query-> | IN ('front-page') AND ( 0 = 1 ) AND wpe21jz2_posts. post_type = 'wp_template' AND ( | | | __construct, WP_Query->query, WP_Query->get_posts | (wpe21jz2_posts. post_status = 'publish')) GROUP BY wpe21jz2_posts. ID ORDER BY wpe21jz2 | | | | _posts. post_date DESC | | 0. 000108 | require_once('wp-includes/template-loader. php'), get_home_template, get_query_template | SELECT wpe21jz2_posts. * FROM wpe21jz2_posts WHERE 1=1 AND wpe21jz2_posts. post_name | | | , locate_block_template, resolve_block_template, get_block_templates, WP_Query->__cons | IN ('home','index') AND ( 0 = 1 ) AND wpe21jz2_posts. post_type = 'wp_template' AND | | | truct, WP_Query->query, WP_Query->get_posts | ((wpe21jz2_posts. post_status = 'publish')) GROUP BY wpe21jz2_posts. ID ORDER BY wpe21j | | | | z2_posts. post_date DESC | | 9. 2E-5 | require_once('wp-includes/template-loader. php'), include('/themes/twentytwentyone/inde | SELECT wpe21jz2_posts. ID FROM wpe21jz2_posts WHERE 1=1 AND ( 0 = 1 ) AND wpe21j | | | x. php'), get_header, locate_template, load_template, require_once('/themes/twentytwent | z2_posts. post_type = 'wp_global_styles' AND ((wpe21jz2_posts. post_status = 'publish')) | | | yone/header. php'), wp_head, do_action('wp_head'), WP_Hook->do_action, WP_Hook->apply_f | GROUP BY wpe21jz2_posts. ID ORDER BY wpe21jz2_posts. post_date DESC LIMIT 0, 1 | | | ilters, wp_enqueue_scripts, do_action('wp_enqueue_scripts'), WP_Hook->do_action, WP_Ho | | | | ok->apply_filters, gutenberg_experimental_global_styles_enqueue_assets, WP_Theme_JSON_ | | | | Resolver_Gutenberg::get_merged_data, WP_Theme_JSON_Resolver_Gutenberg::get_user_data, | | | | WP_Theme_JSON_Resolver_Gutenberg::get_user_data_from_custom_post_type, wp_get_recent_p | | | | osts, get_posts, WP_Query->query, WP_Query->get_posts | | | 0. 000332 | require_once('wp-includes/template-loader. php'), include('/themes/twentytwentyone/inde | SELECT wpe21jz2_posts. ID FROM wpe21jz2_posts WHERE 1=1 AND wpe21jz2_posts. post_typ | | | x. php'), get_footer, locate_template, load_template, require_once('/themes/twentytwent | e = 'post' AND ((wpe21jz2_posts. post_status = 'publish')) ORDER BY wpe21jz2_posts. pos | | | yone/footer. php'), get_template_part, locate_template, load_template, require('/themes | t_date DESC LIMIT 0, 5 | | | /twentytwentyone/template-parts/footer/footer-widgets. php'), dynamic_sidebar, WP_Widge | | | | t->display_callback, WP_Widget_Recent_Posts->widget, WP_Query->__construct, WP_Query-> | | | | query, WP_Query->get_posts | | +----------+----------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------+ --- WordPress incluye, por seguridad, una serie de claves únicas y aleatorias para mejorar la seguridad de acceso al sistema, las llamadas "Salt Keys". Se encuentran en el fichero WP-Config y se parecen a algo tal que esto: define( 'AUTH_KEY', 'Em7t5e2Zq4GgaCff9d3AqtbDHJb4swf6JxAk' ); define( 'SECURE_AUTH_KEY', 'Ct4BtVLmvm2g8cQaBXSTZMz2CX65Uz5SuQNp' ); define( 'LOGGED_IN_KEY', '8qqZZDuBmAGUtVhRM4RkrdhhjGU9jhPFegRg' ); define( 'NONCE_KEY', 'x7B45gtBKjy2GN78PVnxKNQ6ntb579rBQDbX' ); define( 'AUTH_SALT', 'nUu8hgUtAGe8UQxXQzDmfhA42mZzNE6m6NFa' ); define( 'SECURE_AUTH_SALT', '2T4XD65j3Kx2JJcV3NCS3u5RjYNGmv9F5fPt' ); define( 'LOGGED_IN_SALT', 'xvXU9PMZEvMnTc3fe2XjY7wx7XBhAwE9AyZj' ); define( 'NONCE_SALT', 'zy2jyzwQUVq8fXeX5qsFZupNgfep9ptr8j6z' ); En cualquier caso, siempre tenemos la herramienta / API para generarlo. Pero ¿qué ocurre si has de cambiar las claves de todos tus sitios WordPress por algún tipo de cambio de seguridad? Puedes hacerlo de la siguiente manera. Lo primero que definiremos es a partir de qué carpeta ha de buscar en todo el sistema. En este caso va a ser en /webs/: ##### HOST_PATH="/webs/" ##### Actualizaremos WP-CLI: # PONIENDO AL DIA WP-CLI echo "" echo "Actualizando WP-CLI a la última versión:" wp cli check-update --quiet wp cli update --quiet wp cli version Y, si no está disponible, instalará la extensión de FIND. # INSTALANDO WP-CLI FIND echo "" echo "Revisando el buscador de WP-CLI:" if then echo "Buscador de WP-CLI instalado. " else echo "Instalando buscador de WP-CLI:" wp package install wp-cli/find-command --quiet if then echo "Buscador de WP-CLI instalado. " else echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI. " echo "Prueba a instalarlo manualmente. " echo "" echo "wp package install wp-cli/find-command" echo "" exit 1 fi fi Una vez esté, buscaremos todos los sitios del sistema: # BUSCANDO SITIOS echo "" echo "Buscando sitios WordPress:" WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1` WP_TOTAL=`echo "$WP_DATA" | wc -l` echo "Se ha encontrado un total de ${WP_TOTAL} WordPress". Y actualizaremos sus Salt-Keys: echo "" echo "Actualizando Salt-Keys" echo "" WP_SALTS=`wp config shuffle-salts --force --path="${WP_PATH}"` echo "- Estado: ${WP_SALTS}" Y, el código completo: #! /bin/bash ##### HOST_PATH="/webs/" ##### # PONIENDO AL DIA WP-CLI echo "" echo "Actualizando WP-CLI a la última versión:" wp cli check-update --quiet wp cli update --quiet wp cli version # INSTALANDO WP-CLI FIND echo "" echo "Revisando el buscador de WP-CLI:" if then echo "Buscador de WP-CLI instalado. " else echo "Instalando buscador de WP-CLI:" wp package install wp-cli/find-command --quiet if then echo "Buscador de WP-CLI instalado. " else echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI. " echo "Prueba a instalarlo manualmente. " echo "" echo "wp package install wp-cli/find-command" echo "" exit 1 fi fi # BUSCANDO SITIOS echo "" echo "Buscando sitios WordPress:" WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1` WP_TOTAL=`echo "$WP_DATA" | wc -l` echo "Se ha encontrado un total de ${WP_TOTAL} WordPress". for WP_D in $WP_DATA do echo "" echo "********************************************************************************" echo "" echo "WordPress" echo "" WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'` echo "- Ruta: ${WP_PATH}" echo "" echo "Actualizando Salt-Keys" echo "" WP_SALTS=`wp config shuffle-salts --force --path="${WP_PATH}"` echo "- Estado: ${WP_SALTS}" echo "" echo "********************************************************************************" done echo "" echo " -- FIN --" echo "" echo "" --- Cuando decidimos ejecutar los crones en WordPress, hemos de añadir habitualmente una línea por cada uno de los sitios. En el caso de WordPress MultiSite, una línea por cada subsitio. Con este bash podemos indicar el path del WordPress Multiste, que liste todos los sitios incluidos, y que lance los eventos pendientes a ejecutar. #! /bin/bash WP_PATH="/webs/example. com/" SITE_URLS=`wp site list --fields=url --archived=0 --deleted=0 --format=csv --path="$WP_PATH" | sed 1d` for SITE_URL in $SITE_URLS do wp cron event run --due-now --url="$SITE_URL" --path="$WP_PATH" --quiet done --- Cuando se trabaja en bash, una de las herramientas más habituales es la de usar el tabulador para autocompletar comandos. Y WP-CLI incorpora uno. Si queremos que nuestra consola disponga de la funcionalidad, debemos descargarnos la herramienta e implementarla en nuestra cuenta. cd ~ mkdir wp-completion cd wp-completion wget https://github. com/wp-cli/wp-cli/raw/master/utils/wp-completion. bash Una vez descargado, guardaremos el comando para que se pueda ejecutar en el bash. echo "source /home/wordpress/wp-completion/wp-completion. bash" >> ~/. bashrc source ~/. bashrc Y con esto tendremos nuestro autocompletado lanzado con el tabulador. --- Aunque no está recomendado, en algunos entornos es posible que se trabaje como root y que sea necesario lanzar WP-CLI. Cuando ejecutamos WP-CLI como root, nos aparece un mensaje diciendo que para lanzar ese comando hemos de añadir el parámetro --allow-root al comando. Y como se puede hacer pesado, aquí un pequeño cambio para que siempre que lo ejecutemos incluya ese parámetro: vim ~/. bashrc En la parte final del fichero, podemos agregar esto: wp { /usr/local/bin/wp "$@" --allow-root } Lo que hace es modificar el alias de wp por el nuevo, añadiendo a cualquier petición el parámetro. Una vez lo guardemos, ejecutaremos la actualización: source ~/. bashrc --- ¿Sabes donde están los WordPress en tu servidor? ¿Y si alguno tiene actualizaciones pendientes? Con este bash podrás indicar la ruta a partir de la cual buscar los WordPress y listar las actualizaciones que tenga pendientes. Primero indicaremos la ruta a partir de la cual buscar: HOST_PATH="/webs/" Posteriormente actualiza WP-CLI: # PONIENDO AL DIA WP-CLI echo "" echo "Actualizando WP-CLI a la última versión:" wp cli check-update --quiet wp cli update --quiet wp cli version Para buscar WordPress es necesaria la extensión del FIND. En caso de no estar, la instalará. # INSTALANDO WP-CLI FIND echo "" echo "Revisando el buscador de WP-CLI:" if then echo "Buscador de WP-CLI instalado. " else echo "Instalando buscador de WP-CLI:" wp package install wp-cli/find-command --quiet if then echo "Buscador de WP-CLI instalado. " else echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI. " echo "Prueba a instalarlo manualmente. " echo "" echo "wp package install wp-cli/find-command" echo "" exit 1 fi fi Buscaremos todos los sitios: # BUSCANDO SITIOS echo "" echo "Buscando sitios WordPress:" WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1` WP_TOTAL=`echo "$WP_DATA" | wc -l` echo "Se ha encontrado un total de ${WP_TOTAL} WordPress". A partir de aquí, mostramos la información de los WordPress que se encuentren: echo "" echo "WordPress" echo "" WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'` echo "- Ruta: ${WP_PATH}" WP_SITE_NAME=`wp option get blogname --path="${WP_PATH}"` echo "- Nombre: ${WP_SITE_NAME}" WP_SITE_URL=`wp option get siteurl --path="${WP_PATH}"` echo "- URL: ${WP_SITE_URL}" WP_VERSION=`echo "$WP_D" | awk -F, 'NR { print $2 }'` echo "- Versión WP: ${WP_VERSION}" WP_VERSION_UPD=`wp core check-update --path="${WP_PATH}" --format=csv | awk FNR-1 | awk -F, 'NR { print $1 }'` if then echo "- Actualización: ${WP_VERSION_UPD}" fi De los temas: echo "" echo "Themes" echo "" WP_THEMES=`wp theme list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1` WP_TOTAL_THEMES=`echo "$WP_THEMES" | wc -l` echo "- Temas: ${WP_TOTAL_THEMES} temas". for WP_T in $WP_THEMES do WP_THEME_UPD=`echo "${WP_T}" | awk -F, 'NR { print $2 }'` if then WP_THEME_NAME=`echo "${WP_T}" | awk -F, 'NR { print $1 }'` WP_THEME_ACT=`echo "${WP_T}" | awk -F, 'NR { print $3 }'` WP_THEME_NEW=`echo "${WP_T}" | awk -F, 'NR { print $4 }'` echo "- Tema actualizable: ${WP_THEME_NAME} ${WP_THEME_ACT} -> ${WP_THEME_NEW}" fi done Y de los plugins: echo "" echo "Plugins" echo "" WP_PLUGINS=`wp plugin list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1` WP_TOTAL_PLUGINS=`echo "$WP_PLUGINS" | wc -l` echo "- Plugins: ${WP_TOTAL_PLUGINS} plugins". for WP_P in $WP_PLUGINS do WP_PLUGIN_UPD=`echo "${WP_P}" | awk -F, 'NR { print $2 }'` if then WP_PLUGIN_NAME=`echo "${WP_P}" | awk -F, 'NR { print $1 }'` WP_PLUGIN_ACT=`echo "${WP_P}" | awk -F, 'NR { print $3 }'` WP_PLUGIN_NEW=`echo "${WP_P}" | awk -F, 'NR { print $4 }'` echo "- Plugin actualizable: ${WP_PLUGIN_NAME} ${WP_PLUGIN_ACT} -> ${WP_PLUGIN_NEW}" fi done Y el bash script completo aquí: #! /bin/bash ##### HOST_PATH="/webs/" ##### # PONIENDO AL DIA WP-CLI echo "" echo "Actualizando WP-CLI a la última versión:" wp cli check-update --quiet wp cli update --quiet wp cli version # INSTALANDO WP-CLI FIND echo "" echo "Revisando el buscador de WP-CLI:" if then echo "Buscador de WP-CLI instalado. " else echo "Instalando buscador de WP-CLI:" wp package install wp-cli/find-command --quiet if then echo "Buscador de WP-CLI instalado. " else echo "Se ha producido un error. No se ha podido instalar el buscador de WP-CLI. " echo "Prueba a instalarlo manualmente. " echo "" echo "wp package install wp-cli/find-command" echo "" exit 1 fi fi # BUSCANDO SITIOS echo "" echo "Buscando sitios WordPress:" WP_DATA=`wp find $HOST_PATH --format=csv --fields=wp_path,version | awk FNR-1` WP_TOTAL=`echo "$WP_DATA" | wc -l` echo "Se ha encontrado un total de ${WP_TOTAL} WordPress". for WP_D in $WP_DATA do echo "" echo "********************************************************************************" echo "" echo "WordPress" echo "" WP_PATH=`echo "$WP_D" | awk -F, 'NR { print $1 }'` echo "- Ruta: ${WP_PATH}" WP_SITE_NAME=`wp option get blogname --path="${WP_PATH}"` echo "- Nombre: ${WP_SITE_NAME}" WP_SITE_URL=`wp option get siteurl --path="${WP_PATH}"` echo "- URL: ${WP_SITE_URL}" WP_VERSION=`echo "$WP_D" | awk -F, 'NR { print $2 }'` echo "- Versión WP: ${WP_VERSION}" WP_VERSION_UPD=`wp core check-update --path="${WP_PATH}" --format=csv | awk FNR-1 | awk -F, 'NR { print $1 }'` if then echo "- Actualización: ${WP_VERSION_UPD}" fi echo "" echo "Themes" echo "" WP_THEMES=`wp theme list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1` WP_TOTAL_THEMES=`echo "$WP_THEMES" | wc -l` echo "- Temas: ${WP_TOTAL_THEMES} temas". for WP_T in $WP_THEMES do WP_THEME_UPD=`echo "${WP_T}" | awk -F, 'NR { print $2 }'` if then WP_THEME_NAME=`echo "${WP_T}" | awk -F, 'NR { print $1 }'` WP_THEME_ACT=`echo "${WP_T}" | awk -F, 'NR { print $3 }'` WP_THEME_NEW=`echo "${WP_T}" | awk -F, 'NR { print $4 }'` echo "- Tema actualizable: ${WP_THEME_NAME} ${WP_THEME_ACT} -> ${WP_THEME_NEW}" fi done echo "" echo "Plugins" echo "" WP_PLUGINS=`wp plugin list --fields=name,update,version,update_version --format=csv --path="${WP_PATH}" | awk FNR-1` WP_TOTAL_PLUGINS=`echo "$WP_PLUGINS" | wc -l` echo "- Plugins: ${WP_TOTAL_PLUGINS} plugins". for WP_P in $WP_PLUGINS do WP_PLUGIN_UPD=`echo "${WP_P}" | awk -F, 'NR { print $2 }'` if then WP_PLUGIN_NAME=`echo "${WP_P}" | awk -F, 'NR { print $1 }'` WP_PLUGIN_ACT=`echo "${WP_P}" | awk -F, 'NR { print $3 }'` WP_PLUGIN_NEW=`echo "${WP_P}" | awk -F, 'NR { print $4 }'` echo "- Plugin actualizable: ${WP_PLUGIN_NAME} ${WP_PLUGIN_ACT} -> ${WP_PLUGIN_NEW}" fi done echo "********************************************************************************" done echo "" echo " -- FIN --" echo "" echo "" --- ---