This is my biggest bash script yet! It will fully install WordPress from a shell command and add a number of security measures to boot.
It allows you to enter the database connection details, downloads the latest version in your language, file and directory permissions are sorted, it handles the random keys, all the basic WordPress administrator information (if you have WP Cli installed) and a few other things.
- Install and update WordPress in your own language to web root or a sub directory
- Handles file and directory permissions
- Removes licence.txt and readme.html
- Checks for a working database connection
- Adds a random database prefix
- Generates random username and password for administrators
- Set the maximum upload and post limits
- Creates the .htaccess to allow for URL rewriting, adds browser caching and some basic security measures
- Uninstall functionality
- Besides the setup part, this will install WordPress without requiring WP-Cli
#!/bin/sh ################################################################################# # # # WORDPRESS INSTALLER # # # # Coded by Noah Hearle, Design Extreme # # http://designextreme.com # # # # Created: 2015/08/01 # # Modified: 2015/08/06 # # # # Post your comments at: # # https://blog.nahoo.co.uk/wordpress-installer/ # # # # Usage: sh ./wordpress_install.sh <username>[/<subdirectory>] # # [<database-name> <database-username>] <database-password> [<language>] # # # ################################################################################# ## SETTINGS ## upload_max_filesize='64M' language='en_GB' wordpress_download_url='https://en-gb.wordpress.org/wordpress-latest-en_GB.zip' wp_cli=$(type -p "wp") email_regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$" database_name_suffix='_db' database_username_suffix='_user' ## ARGUMENTS ## username=$1 sub_directory='' uninstall=false salt='' random_password='' database_prefix='' database_host=localhost directory='' wordpress_url='' function validate_url() { if [[ `wget -S --spider $1 2>&1 | grep -Ei '200 OK'` ]]; then echo 'true'; else echo 'false'; fi } function generate_random_string() { local l=$1 local letters=$2 if [ ! -n "$letters" ]; then letters='A-Za-z0-9_\[\]()#@-' fi [ "$l" == "" ] && l=16 tr -dc $letters < /dev/urandom | head -c ${l} | xargs } if [ ! -n "$username" ] || [ "$username" == "" ]; then echo -e "\e[1;31mError:\e[0m Please enter at least a username." exit; fi if [[ $username =~ ([/]home[0-9a-z]*[/])?([0-9a-z_-]+)[/]?(public_html|www)?[/]([0-9a-z_-]+) ]]; then username=${BASH_REMATCH[2]} sub_directory=${BASH_REMATCH[4]} elif [[ $username =~ ([/]home.*[/])([0-9a-z_-]+) ]]; then username=${BASH_REMATCH[2]} fi if [ $username != 'help' ] && [ -n $2 ] && [ "$2" == 'uninstall' ]; then uninstall=true elif [ $username != 'help' ] && [ -n "$3" ] && [ "$3" != "" ] && [ -n "$4" ] && [ "$4" != "" ]; then database_name=$2 database_user=$3 database_pass=$4 mysql_connection=$(php -r "\$conn = @mysqli_connect('$database_host', '$database_user', '$database_pass'); if (\$conn && @mysqli_select_db(\$conn, '$database_name')) { echo 1; } else { echo 0; }") if [[ $mysql_connection == 0 ]]; then echo -e "\e[1;31mError:\e[0m Unable to connect to the database using MySQL: $database_name." exit; fi if [ -n "$5" ] && [ "$5" != "" ]; then language=$5 else echo 'Please enter the installation language:'; read -i $language -e language fi elif [ $username != 'help' ] && [ ! -n "$4" ] || [ "$4" == "" ]; then echo 'Please enter the database name:'; read -i "${username}${database_name_suffix}" -e database_name echo 'Please enter the database username:'; read -i "${username}${database_username_suffix}" -e database_user echo 'Please enter the database password:'; read database_pass mysql_connection=$(php -r "\$conn = @mysqli_connect('$database_host', '$database_user', '$database_pass'); if (\$conn && @mysqli_select_db(\$conn, '$database_name')) { echo 1; } else { echo 0; }") if [[ $mysql_connection == 0 ]]; then echo -e "\e[1;31mError:\e[0m Unable to connect to the database using MySQL: $database_name." exit; fi if [ -n "$3" ] && [ "$3" != "" ]; then language=$3 else echo 'Please enter the installation language:'; read -i $language -e language fi else echo -e '\e[1;44mUsage:\e[0m sh ./wordpress_install.sh <username>[/<subdirectory>] [<database-name> <database-username>] <database-password> [<language>]'; exit; fi if [ ! -d /home/$username/public_html/ ]; then echo -e "\e[1;31mError:\e[0m Unable to locate username: $username." exit; fi if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then if [ ! -d /home/$username/public_html/$sub_directory/ ]; then echo -e "\e[1;31mError:\e[0m Unable to locate sub directory: $sub_directory." exit; fi directory="/home/$username/public_html/$sub_directory/" else directory="/home/$username/public_html/" fi cd $directory; if [ -f wp-config.php ] && [ $uninstall == false ]; then echo -e '\e[1mInstall Failed\e[0m' if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then echo -e "\e[1;31mError:\e[0m WordPress installation exists for username: $username, sub directory: $sub_directory." exit; fi echo -e "\e[1;31mError:\e[0m WordPress installation exists for username: $username." exit; elif [ ! -f wp-config.php ] && [ $uninstall == true ]; then echo -e '\e[1mUninstall Failed\e[0m'; if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then echo -e "\e[1;31mError:\e[0m Cannot find an existing WordPress installation for username: $username, sub directory: $sub_directory." exit; fi echo -e "\e[1;31mError:\e[0m Cannot find an existing WordPress installation for username: $username." exit; fi if [ $uninstall == true ]; then rm -Rf wp-*; rm -f xmlrpc.php; rm -f license.txt; rm -f readme.html; rm -f index.php; touch .htaccess; touch index.php; chown $username.$username .htaccess; chown $username.$username index.php; cat >.htaccess <<EOL <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^.*$ /index.php [L] </IfModule> EOL cat >index.php <<EOL <!doctype html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> </body> </html> EOL echo -e '\e[1mUninstall Successful\e[0m' if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then echo -e "\e[92mSuccess:\e[0m Removed files for the existing WordPress installation for username: $username, sub directory: $sub_directory." exit; fi echo -e "\e[92mSuccess:\e[0m Removed files for the existing WordPress installation for username: $username." exit; fi if [[ `validate_url "$wordpress_download_url" >/dev/null` == false ]]; then echo -e '\e[1mInstall Failed\e[0m' echo -e "\e[1;31mError:\e[0m Cannot find the WordPress installation file at: $wordpress_download_url." exit; fi database_prefix="wp$(generate_random_string 4 a-z)_" wget --progress=bar:force "$wordpress_download_url" -O wordpress-latest.zip; if [ ! -f "${directory}wordpress-latest.zip" ]; then echo -e "\e[1;31mError:\e[0m Unable to locate installation file: ${directory}wordpress-latest.zip." exit; fi unzip -q wordpress-latest.zip; if [ ! -d "${directory}wordpress/" ]; then echo -e "\e[1;31mError:\e[0m Unable to locate extracted files: ${directory}wordpress/." exit; fi rm -f wordpress-latest.zip; chown -R $username.$username wordpress/; mv -f wordpress/* ./; rm -Rf wordpress/; mkdir wp-content/cache/; mkdir wp-content/uploads/; mkdir wp-content/w3tc-config/; chown -R nobody.nobody wp-content/cache/; chown -R nobody.nobody wp-content/uploads/; chown -R nobody.nobody wp-content/w3tc-config/; rm -f license.txt; rm -f readme.html; if [ $wp_cli ]; then echo 'Please enter the URL of the WordPress website:'; read -i 'http://' -e wordpress_url wordpress_url=$(sed -e 's/[\\]/\\&/g' <<< $wordpress_url) database_name=$(sed -e 's/[\\]/\\&/g' <<< $database_name) database_user=$(sed -e 's/[\\]/\\&/g' <<< $database_user) database_pass=$(sed -e 's/[\\]/\\&/g' <<< $database_pass) rm -f wp-config-sample.php; wp --allow-root core config --path="$directory" --url="$wordpress_url" --dbname="$database_name" --dbuser="$database_user" --dbpass="$database_pass" --locale="$language" --dbprefix="$database_prefix" chown $username.$username wp-config.php; else salt=$(perl -i -p0e 's/(;)[\s\r\n ]*(define)/\1\n\2/smg' <<< $(sed -e 's/[^0-9a-z@\s\n ,.:;_-]/\\&/gi' <<< $(curl -L --silent "https://api.wordpress.org/secret-key/1.1/salt/"))); database_name=$(sed -e 's/[]\/\\&%$*.^|[]/\\&/g' <<< $database_name) database_user=$(sed -e 's/[]\/\\&%$*.^|[]/\\&/g' <<< $database_user) database_pass=$(sed -e 's/[]\/\\&%$*.^|[]/\\&/g' <<< $database_pass) mv -f wp-config-sample.php wp-config.php; sed -i "s/database_name_here/$database_name/" wp-config.php; sed -i "s/username_here/$database_user/" wp-config.php; sed -i "s/password_here/$database_pass/" wp-config.php; fi sed -i "s/\(\$table_prefix\s*=\s*'\)[a-z_-]+\(';\)/\1$database_prefix\2/" wp-config.php; if [ -n "$salt" ]; then perl -0pi -e "s/define[(].AUTH_KEY.+\n.+\n.+\n.+\n.+\n.+\n.+\n.+NONCE_SALT.+/$salt/" wp-config.php; fi if [ -n $language ] && [[ ! -n $(grep -Eq "define\('WPLANG'" wp-config.php) ]]; then perl -0pi -e "s/([\/* \t]+That.s all.+)/\n\n\/* WordPress Localized Language *\/\ndefine('WPLANG', '$language');\n\n$&/" wp-config.php fi if [[ ! -n $(grep -Eq "define\('AUTO_UPDATE'" wp-config.php) ]]; then perl -0pi -e "s/([\/* \t]+That.s all.+)/\n\n\/* Server-wide automatic update service *\/\ndefine('AUTO_UPDATE', TRUE);\n\n$&/" wp-config.php fi if [ ! -f .htaccess ]; then touch .htaccess; fi cat >.htaccess <<EOL ErrorDocument 401 default # BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_URI} ^/wp-content/uploads/.*$ RewriteRule ^wp-content/uploads/.*\.(php|inc|rb|py|s?html?|js|vb|cgi|pl)$ - [F,L,NC] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress # BEGIN W3TC Browser Cache <IfModule mod_deflate.c> <IfModule mod_headers.c> Header append Vary User-Agent env=!dont-vary </IfModule> AddOutputFilterByType DEFLATE text/css text/x-component application/x-javascript application/javascript text/javascript text/x-js text/html text/richtext image/svg+$ <IfModule mod_mime.c> # DEFLATE by extension AddOutputFilter DEFLATE js css htm html xml </IfModule> </IfModule> # END W3TC Browser Cache <Files wp-config.php> Order allow,deny Deny from all </Files> php_value upload_max_filesize $upload_max_filesize php_value post_max_size $upload_max_filesize EOL if [ $wp_cli ]; then random_user=User_$(generate_random_string 5 A-Za-z0-9) random_password=$(generate_random_string 10) if [ ! -n $wordpress_url ] || [ $wordpress_url == '' ]; then echo 'Please enter the URL of the WordPress website:'; read -i 'http://' -e wordpress_url fi echo 'Website title:'; read wordpress_title echo 'Username for the administrator:'; read -i $random_user -e admin_user echo 'Password for the administrator:'; read -i $random_password -e admin_password echo 'Email address for the administrator:'; read admin_email wordpress_url=$(sed -e 's/[\\]/\\&/g' <<< $wordpress_url) wordpress_title=$(sed -e 's/[\\]/\\&/g' <<< $wordpress_title) admin_user=$(sed -e 's/[\\]/\\&/g' <<< $admin_user) admin_password=$(sed -e 's/[\\]/\\&/g' <<< $admin_password) if [[ $admin_email =~ $email_regex ]]; then admin_email=$(sed -e 's/[\\]/\\&/g' <<< $admin_email) else echo 'Please enter a valid email address for the administrator:'; read admin_email if [[ $admin_email =~ $email_regex ]]; then admin_email=$(sed -e 's/[\\]/\\&/g' <<< $admin_email) else echo -e "\e[1;31mError:\e[0m Invalid email address: $admin_email. Skipping setup." admin_email='' fi fi if [ -n $admin_email ] && [ $admin_email != '' ]; then wp --allow-root --quiet --path="$directory" --url="$wordpress_url" --title="$wordpress_title" --admin_user="$admin_user" --admin_password="$admin_password" --admin_email="$admin_email" core install; wp --allow-root --quiet --path="$directory" theme update --all; wp --allow-root --quiet --path="$directory" plugin update --all; echo -e '\e[1mInstall and Setup Successful\e[0m'; if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then echo -e "\e[92mSuccess:\e[0m Installed and setup WordPress for username: $username, sub directory: $sub_directory." exit; fi echo -e "\e[92mSuccess:\e[0m Installed and setup WordPress for username: $username." exit; fi fi echo -e '\e[1mInstall Successful\e[0m'; if [ -n "$sub_directory" ] && [ "$sub_directory" != "" ]; then echo -e "\e[92mSuccess:\e[0m Installed WordPress for username: $username, sub directory: $sub_directory." exit; fi echo -e "\e[92mSuccess:\e[0m Installed WordPress for username: $username." exit;
It is simple to use, to install a new WordPress website run:
sh ./wordpress_install.sh <username>[/<subdirectory>] [[<database-name> <database-username>] <database-password> [<language>]]
The arguments in square brackets are optional, you just need the username as a minimum. To uninstall (remove all files and reset the website) an existing installation use:
sh ./wordpress_install.sh <username>[/<subdirectory>] uninstall
Please have a look at my automatic WordPress update script if you like to set up regular updates using WP-Cli. In a future version of this script, I’ll make the arguments easier to set and include all the inputs needed for the WP-Cli setup.
If you’re adding this to your server, I’d recommend taking the opportunity to add a few of your favourite plugins as follows:
cp -Rf /another/blog/wp-content/plugins/plugin-name ${directory}wp-content/plugins/; unzip -q /source/directory/another-plugin.zip -d ${directory}wp-content/plugins/; chown -R $username.$username ${directory}wp-content/plugins/; wp --allow-root --path="$directory" plugin activate plugin-name; wp --allow-root --path="$directory" plugin activate another-plugin;
Insert this after the wp core install
line and edit this to use your plugin names and locations.
Please send me your suggestions and I’ll update the script if you have some good ideas!
Update on 5th November 2018: Added a check for a working MySQL connection before attempting to install WordPress. Previously, a bad database connection broke the installation process.
Last updated on