WordPress Bash Upgrade Script

Submitted on Jan 18, 2013, 6:08 a.m.

Bash is fun. It's a little weird, but it's fun. I've been reading the Linux Command Line and Shell Scripting Bible which I highly recommend. I also wanted a script I could use to update the multiple WordPress installations I'm now hosting.

I found Liz Quilty's handy WordPress mass update script 3.4.1, but wanted to refactor the script to use functions, curl, and tar (as well as remove support for WordPress MU)

And so here it is, one of a handful of Bash script exercises I've completed to-date. Enjoy, and thanks Liz for the head start.

First, a machine specific configuration file (read the warning in the comments section of the script below). Place the config file next to the script file; that is, place wp-upgrade.conf in the same directory as wp-upgrade.sh.

1# wp-upgrade.conf
2# Make sure you set the FIND_DIR variable to the directory your websites are located.
3# Usually this is /var/www but occasionally its /home (LQ)
4# Do not set it to be / because then it will also find your 'backups' and possibly
5# overwrite them (LQ)
6FIND_DIR=/var/www
7
8# Set this to the latest version of WordPress. This will be used as the comparison
9# with your existing installations to determine if they require upgrading.
10WP_CURRENT_VER="3.5.1"
11WP_URL=http://wordpress.org/latest.tar.gz
12WP_SOURCE_DIR=/tmp/wpupgrade
13MYSQL=/usr/local/bin/mysql
14MYSQL_ADMIN=/usr/local/bin/mysqladmin
15MYSQL_DUMP=/usr/local/bin/mysqldump

And now the script...

1#!/bin/bash
2
3# WordPress core upgrade script (plugins will still have to be updated separately).
4
5# (c) Copyright (c) 2012, Anthony Bouch (tony@58bits.com). All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27
28# Based on a script written by Liz Quilty ( liz@rimuhosting.com )
29# Run as root
30# sudo sh wp-upgrade.sh
31
32# version 1.4 - refactored to use functions, curl and tar and removed WordPress MU
33# support (AB).
34# version 1.3 - keeping the permissions so that the web user can write to things OK (LQ)
35# version 1.2 - Patched for better portability by http://twitter.com/valthonis (LQ)
36
37#Set exit on any error.
38set -e
39
40# Source our configuration file.
41# NOTE - this is easily broken by path discoverable script sources, symlinks etc.
42# So be sure to only call the upgrade script with this in mind.
43# http://mywiki.wooledge.org/BashFAQ/028
44script_source=$([[ $0 == /* ]] && echo "$0" || echo "${PWD}/${0#./}")
45config_source=${script_source%".sh"}".conf"
46source $config_source
47
48###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
49#
50# FUNCTION: get_owner_group
51# Get the ownership and group values for a directory or a filename
52# and returns a value that can be used for chown.
53# Params: $1 = fully qualified directory or filename.
54# NOTE: May not work for symlinked directories.
55#
56###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
57get_owner_group() {
58 if [ -d "$1" ]; then
59 echo $(ls -lah $1 | awk '{print $3,$4}'| sed -n -e 's/\ /:/' -e '2p')
60 elif [ -f "$1" ]; then
61 echo $(ls -lah $1 | awk '{print $3,$4}'| sed 's/\ /:/')
62 else
63 echo "get_owner_group requires a valid directory or filename as an argument." >&2
64 exit 1
65 fi
66}
67
68###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
69#
70# FUNCTION: get_wp_root
71# Get the installation directory for WordPress
72# Params: $1 = fully qualified filename for version.php
73#
74###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
75get_wp_root() {
76 if [ -z "$1" -o ! -s "$1" ]; then
77 echo "get_wp_root requires a valid filename as an argument." >&2
78 exit 1
79 fi
80 echo "$1" | sed s@wp-includes/version.php@@
81}
82
83###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
84#
85# FUNCTION: get_installed_version
86# Get the installed version of WordPress
87# Params: $1 = fully qualified filename for version.php
88#
89###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
90get_installed_version() {
91 if [ -z "$1" -o ! -s "$1" ]; then
92 echo "get_installed_version requires a valid filename as an argument." >&2
93 exit 1
94 fi
95 # Fixed for version 3.5 (which is not 5 characters long)
96 # echo grep wp_version "$1" | grep -v global | cut -c16-20
97 echo $(grep '^\$wp_version' "$1" | cut -d "'" -f 2)
98}
99
100
101###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
102#
103# FUNCTION: get_database_settings
104# Get the database settings for this installation
105# Params: $1 = the root directory of the WordPress installation
106#
107###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
108get_database_settings() {
109 if [ -z "$1" -o ! -d "$1" ]; then
110 echo "get_database_settings requires a valid directory as an argument." >&2
111 exit 1
112 fi
113
114 db_name=$(grep DB_NAME "${1}/wp-config.php" |cut -d "'" -f 4)
115 db_user=$(grep DB_USER "${1}/wp-config.php" | cut -d "'" -f 4)
116 db_pass=$(grep DB_PASSWORD "${1}/wp-config.php" | cut -d "'" -f 4)
117 table_prefix=$(grep '$table_prefix' "${1}/wp-config.php" | cut -d "'" -f 2)
118
119 echo $db_name $db_user $db_pass $table_prefix
120}
121
122###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
123#
124# FUNCTION: get_latest_wp
125# Download the latest version of WordPress
126# Params: None
127# Notes: Needs better error handling
128#
129###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
130get_latest_wp() {
131 mkdir -p $WP_SOURCE_DIR
132 cd $WP_SOURCE_DIR
133 echo 'Getting latest version of WordPress... (removing previous download if any).'
134 rm -rf ${WP_SOURCE_DIR}/latest.tar.gz
135 rm -rf ${WP_SOURCE_DIR}/wordpress
136 curl -O $WP_URL && tar -xmzf latest.tar.gz && chown -R $(get_owner_group $WP_SOURCE_DIR) ${WP_SOURCE_DIR}/wordpress/
137 if [ $? -ne 0 ]; then
138 echo "Download of the latest version of WordPress failed."
139 exit 1
140 fi
141 cd - > /dev/null
142 echo 'Done.'
143}
144
145###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
146#
147# FUNCTION: do_upgrade
148# Perform the core WordPress upgrade on a discovered WordPress installation
149# Params: $1 = target directory
150#
151###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
152do_upgrade() {
153 if [ -z "$1" -o ! -d "$1" ]; then
154 echo "do_upgrade requires a valid directory as an argument."
155 exit 1
156 fi
157
158 wp_root=$1
159 echo "Upgrading $wp_root"
160
161 settings=$(get_database_settings $wp_root)
162
163 db_name=$(echo "$settings" | awk '{print $1}')
164 db_user=$(echo "$settings" | awk '{print $2}')
165 db_pass=$(echo "$settings" | awk '{print $3}')
166 table_prefix=$(echo "$settings" | awk '{print $4}')
167
168 echo Checking connection to the database.
169 db_test=$($MYSQL_ADMIN -u${db_user} -p${db_pass} ping)
170
171 if [ "$db_test" == "mysqld is alive" ]; then
172 echo Database connects fine.
173 # 1. Get clean url and sitename for site.
174 siteurl=$(echo SELECT option_value FROM ${table_prefix}options WHERE option_name=\'siteurl\' LIMIT 1 | $MYSQL -u ${db_user} -p${db_pass} ${db_name} | sed s/option_value//)
175 echo Site URL is $siteurl
176 clean_url=$(echo $siteurl| sed s@http://@@g)
177 sitename=$(echo SELECT option_value FROM ${table_prefix}options WHERE option_name=\'blogname\' LIMIT 1 | $MYSQL -u ${db_user} -p${db_pass} ${db_name} | sed s/option_value//)
178 echo Site name is $sitename
179 clean_sitename=${sitename//[[:space:]]/}
180
181 # 2. Backup the database for the site.
182 echo Making backup at /var/wp_upgrade/${clean_sitename}.sql and /var/wp_upgrade/${clean_sitename}.tar.gz \(you can delete these later\)
183 mkdir -p /var/wp_upgrade/${clean_sitename}
184 $MYSQL_DUMP -u ${db_user} -p${db_pass} ${db_name} > /var/wp_upgrade/${clean_sitename}/${clean_sitename}.sql &&
185
186 # 3. Take a complete backup of the WordPress site directory.
187 tar -czf /var/wp_upgrade/${clean_sitename}/${clean_sitename}.tar.gz ${wp_root}
188
189 # 4. Copying core files into site, getting the original owner permissions
190 orig_perm=$(get_owner_group ${wp_root}/wp-content)
191 alias cp=cp #some distros have cp aliased to cp -i which asks before each overwrite
192 echo "Setting up maintenance mode ..."
193 touch $wp_root/.mainenance
194 echo Copying files over ...
195
196 # http://codex.wordpress.org/Updating_WordPress
197 rm -r ${wp_root}/wp-includes
198 rm -r ${wp_root}/wp-admin
199 cp -a ${WP_SOURCE_DIR}/wordpress/* $wp_root/
200
201 echo Changing ownership back to $orig_perm
202 chown -R $orig_perm $wp_root
203
204 #echo You may need to go to $siteurl/wp-admin/upgrade.php to complete the upgrade
205 echo Triggering database upgrade with curl --silent $siteurl/wp-admin/upgrade.php?step=1 > /dev/null 2>&1
206 curl --silent $siteurl/wp-admin/upgrade.php?step=1 >/dev/null 2>&1
207
208 echo "Going back to normal mode..."
209 rm $wp_root/.mainenance
210 echo "Upgrade at $wp_root complete."
211 else
212 echo "Unable to connect to the database for this site. Please upgrade manually."
213 fi
214}
215
216###~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
217# Let's start...
218if [ $(whoami) != "root" ]
219 then
220 echo "You need to run this script as root (preferably via sudo)."
221 exit 1
222fi
223
224# Download and extract the latest version of WordPress
225get_latest_wp
226
227# Find all of our target WordPress installations
228wplist=$(find $FIND_DIR -wholename "*wp-includes/version.php")
229
230for file in $wplist ; do
231 wp_root=$(get_wp_root $file)
232 installed_ver=$(get_installed_version $file)
233
234 if [ ${installed_ver} != ${WP_CURRENT_VER} ];then
235 echo "You have version $installed_ver located at $wp_root that needs upgrading to $WP_CURRENT_VER"
236 echo -n "Would you like to upgrade it? [y/N] "
237 read ans
238 if [ ! -z "$ans" -a "$ans" == "y" ];then #Fixed: Default non key entry now correctly equivalent to No.
239 do_upgrade $wp_root
240 else
241 echo "Skipping ${wp_root}."
242 fi
243 else
244 echo "Located WordPress at $wp_root. This installation is up-to-date."
245 fi
246done