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

