Nginx-WordPress installer for Debian I have written the following Bash script which includes an Nginx WordPress bootstrapper: A program establish WordPress apps fast on a Debian-Nginx environment.

It utilizes CertBot for HTTPS, SSHguard for protection from BFA (see Nginx conf), WP-CLI for creating a WP app dir and conf file fast, MySQL CLI to create a dbstack and a local function named rse to restart the server and giving proper permissions to the app dir.

#!/bin/bash
domain="$1" &amp;&amp; test -z "$domain" &amp;&amp; exit 2

environment() {
    read -sp "Please enter the app DB root password:      " dbrootp_1 &amp;&amp; echo
    read -sp "Please enter the app DB root password again:" dbrootp_2 &amp;&amp; echo
    if [ "$dbrootp_1" != "$dbrootp_2" ]; then echo "Values unmatched. Please try again." &amp;&amp; exit 2 fi

    read -sp "Please enter the app DB user password:      " dbuserp_1 &amp;&amp; echo
    read -sp "Please enter the app DB user password again:" dbuserp_2 &amp;&amp; echo
    if [ "$dbuserp_1" != "$dbuserp_2" ]; then echo "Values unmatched. Please try again." &amp;&amp; exit 2 fi
}
environment

wordpress() {
    rm -rf "$drt"/"$domain"/ 2&gt;/dev/null
    wp core download --path="$drt"/"$domain"/ --allow-root
    wp config create --path="$drt"/"$domain"/ --dbname="$domain" --dbuser="$domain" --dbpass="$dbuserp" --dbhost="localhost" --allow-root
}
wordpress

nginx() {
    rm "$s_a/$domain.conf" 2&gt;/dev/null
    rm "$s_e/$domain.conf" 2&gt;/dev/null

    cat &lt;&lt;-EOF &gt; "$s_a/$domain.conf"
        server {
            root ${drt}/${domain}/;
            server_name ${domain} www.${domain};
            location ~* \.(jpg|jpeg|png|gif|ico|css|js|ttf|woff|pdf)$ {expires 365d;}
            location "/wp-login\.php" {access_log "/var/log/httpd/wordpress-logins.log";}
        }
    EOF

    ln -sf "$s_a"/"$domain".conf "$s_e"
    rse
}
nginx

certbot() {
    certbot --nginx -d "$domain" -d www."$domain"
    rse
}
certbot

database() {
    cat &lt;&lt;-EOF | mysql -u root -p"$dbrootp_1"
        DROP USER IF EXISTS "$domain"@"localhost";
        DROP database IF EXISTS "$domain";
        CREATE USER "$domain"@"localhost" IDENTIFIED BY "$dbuserp_1";
        CREATE DATABASE "$domain";
        GRANT ALL PRIVILEGES ON "$domain".* TO "$domain"@"localhost";
    EOF
}
database

finalize() {
    echo "Change http to http2 in your Nginx app conf and run rse"
}
finalize


Note

Before executing the script I declared in the end of $HOME/.bashrc:

drt="/var/www/html"
s_a="etc/nginx/sites-available"
s_e="etc/nginx/sites-enabled"
My answer is going to focus on your use of shell functions and global variables.  There aren't any actual syntax errors in your script AFAICT, but your misuse of functions in this script is dangerously error-prone.

There are two main reasons for using a function in bash (or any language, really).

The first is to parameterise a frequently performed operation - e.g. by passing arguments to it rather than hard-coding values (or relying on side-effects).  Optionally it should return a result to the caller (in shell, by printing something to stdout to be captured with command substitution.  Or sometimes by changing a variable in the environment - but see below about that)

You're not doing that.

Worse, you're making your functions rely on global variables.  This makes your functions (and your scripts) dependent on side-effects of previous variable definitions - which may still hold old values from previous runs (and you forgot to update them for the current run) or, worse, may be empty because they haven't been initialised yet in the current shell.  This will inevitably lead to catastrophic data-loss one day when you forget to set a value for "$drt" and/or "$domain" and/or your many other global variables.

Better to pass values as parameters when and as needed. Don't trust that a crucial variable happened to have been set some time in the past - i.e. be explicit, not implicit.

As a rule, functions should be self-contained and not rely on variables that may or may not be defined outside of their scope.  They should also define variables as being local to them so that they explicitly don't use (or worse, change) a global variable that happens to have the same name as the function's variable.

Anyway, an example of how your wordpress function could be written:

wordpress() {
    local drt="$1"
    local domain="$2"
    local dbpass="$3"

    # should do more checking here, this is the bare minimum.
    # it avoids running `rm -rf /` if both $drt and $domain
    # are empty, and running `rm -rf "$drt/"` if $domain is empty.
    [ -z "$drt"    ] &amp;&amp; error 1 '$drt is empty. aborting!'
    [ -z "$domain" ] &amp;&amp; error 1 '$domain is empty. aborting!'
    [ -z "$dbpass" ] &amp;&amp; error 1 '$dbpass is empty. aborting!'

    # should check exit status of each command run here and take
    # appropriate action on any failures.  Figure out what you want
    # to happen if any of the following fail and implement it.
    rm -rf "$drt"/"$domain"/ 2&gt;/dev/null
    wp core download --path="$drt"/"$domain"/ --allow-root
    wp config create --path="$drt"/"$domain"/ --dbname="$domain" \
      --dbuser="$domain" --dbpass="$dbpass" --dbhost=localhost \
      --allow-root
}


You'd call it as wordpress "$drt" "$domain" "$dbpass".  It uses a function called error:

error() {
  local ec msg

  ec="$1" ; shift
  msg="$*"

  [ -n "$msg" ] &amp;&amp; echo "$msg" &gt;&amp;2
  # don't exit if $ec==0 - just return after printing a warning to stderr
  [ "$ec" != 0 ] &amp;&amp; exit "$ec"
}


The second reason is to make the structure of the script easier to follow.  For example, instead of having a lot of shell code for each pattern matched in a case statement (which makes it difficult to see at a glance what the cases are and what each one does), write a function for each case and call it - with or without arguments.

You're not doing that either - you're using each function as soon as you define it.  That's both weird and pointless....especially so since your functions don't take arguments.

A more usual way of defining and using functions would be something like:

function1 () { ... ; }
function2 () { ... ; }
function3 () { ... ; }
function4 () { ... ; }

main () {
  function1
  function2
  function3
  function4
}

main


Note: using amain function allows you to define and call functions in any order, and allows you to define your functions (other than main() itself) at the end of the script so that the main code comes first.  Otherwise you can get errors in situations where, e.g., your main non-function script code calls function2 before it is defined.

as noted above, each function call can, or should, have explicit arguments passing in the values they will use.

BTW, for the case statement mentioned above:

case "$var" in
  1) function1 ;;
  2) function2 ;;
  3) function3 ;;
  4) function4 ;;
  *) error 1 'Unknown case' ;;
esac


That case statement would be a lot harder to read if each case had 5 or 10 or more lines of code.