diff --git a/contrib/depdot.sh b/contrib/depdot.sh
index c065ea57b05e81fb13b02c0e6d6a39c4a12f32ce..4e0a940685ba7446177f909866e017d72dbd2ab9 100755
--- a/contrib/depdot.sh
+++ b/contrib/depdot.sh
@@ -5,8 +5,7 @@
 #  * Works only if directory names and package names are the same (true for all Gluon packages)
 #  * Doesn't show dependencies through virtual packages correctly
 
-
-
+set -e
 shopt -s nullglob
 
 
diff --git a/contrib/lsupgrade.sh b/contrib/lsupgrade.sh
index eca7a852f444f09ea443e16adc4ca7664aa9ba4a..f8e28f9a6d800924c474f8af888d127c5b1ea86a 100755
--- a/contrib/lsupgrade.sh
+++ b/contrib/lsupgrade.sh
@@ -1,5 +1,6 @@
 #!/bin/bash
 
+set -e
 # Script to list all upgrade scripts in a clear manner
 # Limitations:
 #  * Does only show scripts of packages whose `files'/`luasrc' directories represent the whole image filesystem (which are all Gluon packages)
@@ -27,7 +28,7 @@ fi
 
 pushd "$(dirname "$0")/.." >/dev/null
 
-find ./package packages -name Makefile | while read makefile; do
+find ./package packages -name Makefile | while read -r makefile; do
 	dir="$(dirname "$makefile")"
 
 	pushd "$dir" >/dev/null
diff --git a/contrib/sign.sh b/contrib/sign.sh
index c04854e81e7936beef6010f8de263588b74284f8..d88bc345d8436a23a05da724c9d8c03add798baa 100755
--- a/contrib/sign.sh
+++ b/contrib/sign.sh
@@ -2,7 +2,7 @@
 
 set -e
 
-if [ $# -ne 2 -o "-h" = "$1" -o "--help" = "$1" -o ! -r "$1" -o ! -r "$2" ]; then
+if [ $# -ne 2 ] || [ "-h" = "$1" ] || [ "--help" = "$1" ] || [ ! -r "$1" ] || [ ! -r "$2" ]; then
 	cat <<EOHELP
 Usage: $0 <secret> <manifest>
 
diff --git a/contrib/sigtest.sh b/contrib/sigtest.sh
index 2a0faf2cf15ca3181c0739f365ee8b8a7a8cd1a7..0fa19f90f3d089a705b30e54888de5f4a3078638 100755
--- a/contrib/sigtest.sh
+++ b/contrib/sigtest.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-if [ $# -eq 0 -o "-h" = "$1" -o "-help" = "$1" -o "--help" = "$1" ]; then
+if [ $# -eq 0 ] || [ "-h" = "$1" ] || [ "-help" = "$1" ] || [ "--help" = "$1" ]; then
     cat <<EOHELP
 Usage: $0 <public> <signed manifest>
 
@@ -27,7 +27,7 @@ awk "BEGIN    { sep=0 }
                 else       print > \"$lower\"}" \
     "$manifest"
 
-while read line
+while read -r line
 do
     if ecdsaverify -s "$line" -p "$public" "$upper"; then
         ret=0
diff --git a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
index 0778b354847070cddf43ef742274e4b2846ef137..708e9743eb69499f8dca251cc0d4dd3a0afa8fe0 100755
--- a/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
+++ b/package/gluon-core/files/lib/netifd/proto/gluon_wired.sh
@@ -14,6 +14,7 @@ xor2() {
         echo -n "${1:1:1}" | tr '0123456789abcdef' '23016745ab89efcd'
 }
 
+# shellcheck disable=SC2086
 interface_linklocal() {
         local macaddr="$(ubus call network.device status '{"name": "'"$1"'"}' | jsonfilter -e '@.macaddr')"
         local oldIFS="$IFS"; IFS=':'; set -- $macaddr; IFS="$oldIFS"
diff --git a/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode b/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode
index 3a3cb20f86c36d87a2bfdc3e904f4bcc8771adfd..314f8a4310153a6f4087486da90961d60057268c 100755
--- a/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode
+++ b/package/gluon-setup-mode/files/etc/hotplug.d/button/50-gluon-setup-mode
@@ -12,7 +12,7 @@ wait_setup_mode() {
 }
 
 
-if [ "$BUTTON" = wps -o "$BUTTON" = reset -o "$BUTTON" = phone ]; then
+if [ "$BUTTON" = wps ] || [ "$BUTTON" = reset ] || [ "$BUTTON" = phone ]; then
 	case "$ACTION" in
 		pressed)
 			wait_setup_mode &
@@ -21,7 +21,7 @@ if [ "$BUTTON" = wps -o "$BUTTON" = reset -o "$BUTTON" = phone ]; then
 			;;
 		released)
 			if [ -r /tmp/.wait_setup_mode ]; then
-				kill $(cat /tmp/.wait_setup_mode)
+				kill "$(cat /tmp/.wait_setup_mode)"
 				rm /tmp/.wait_setup_mode
 			fi
 			;;
diff --git a/package/gluon-setup-mode/files/lib/preinit/90_setup_mode b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode
index 396b4f767d22a38554e5d501eeb693fde902641c..c38ac281274c72856fddda7a16a9760cee60309a 100644
--- a/package/gluon-setup-mode/files/lib/preinit/90_setup_mode
+++ b/package/gluon-setup-mode/files/lib/preinit/90_setup_mode
@@ -5,7 +5,7 @@ setup_mode_enable() {
 	local enabled="$(uci -q get 'gluon-setup-mode.@setup_mode[0].enabled')"
 	local configured="$(uci -q get 'gluon-setup-mode.@setup_mode[0].configured')"
 
-	if [ "$enabled" = 1 -o "$configured" != 1 ]; then
+	if [ "$enabled" = 1 ] || [ "$configured" != 1 ]; then
 		echo '/lib/gluon/setup-mode/rc.d' > /tmp/rc_d_path
 	fi
 }
diff --git a/scripts/features.sh b/scripts/features.sh
index 2d35fdeeb58c3f9e51b02b66e94343b6584bacd5..1d7184eeb0bda2d3ed3a3666fbca3d651fe1cfac 100755
--- a/scripts/features.sh
+++ b/scripts/features.sh
@@ -31,14 +31,14 @@ sanitize() {
 	echo -n "$v"
 }
 
-vars=
+vars=()
 
 for feature in $1; do
-	if [ "$(type -t gluon_feature_nodefault_${feature})" != 'function' ]; then
+	if [ "$(type -t "gluon_feature_nodefault_${feature}")" != 'function' ]; then
 		echo "gluon-${feature}"
 	fi
 
-	vars="$vars $(sanitize "$feature")=1"
+	vars+=("$(sanitize "$feature")=1")
 done
 
 
@@ -46,18 +46,19 @@ nodefault() {
 	:
 }
 
+# shellcheck disable=SC2086
 packages() {
 	local cond="$(sanitize "$1")"
 	shift
 
 	# We only allow variable names, parentheses and the operators: & | !
-	if [ "$(expr match "$cond" '.*[^A-Za-z0-9_()&|! ].*')" -gt 0 ]; then
+	if grep -q '[^A-Za-z0-9_()&|! ]' <<< "$cond"; then
 		exit 1
 	fi
 
 	# Let will return false when the result of the passed expression is 0,
 	# so we always add 1. This way false is only returned for syntax errors.
-	local ret="$(env -i $vars bash --norc -ec "let _result_='1+($cond)'; echo -n \"\$_result_\"" 2>/dev/null)"
+	local ret="$(env -i "${vars[@]}" bash --norc -ec "let _result_='1+($cond)'; echo -n \"\$_result_\"" 2>/dev/null)"
 	case "$ret" in
 	2)
 		for pkg in "$@"; do
diff --git a/scripts/filesize.sh b/scripts/filesize.sh
index 1a6ea4dd92be992b8a0a3fb9e481134a2856bc1b..7068eb544fcec5172279469f873acdc781390a2d 100755
--- a/scripts/filesize.sh
+++ b/scripts/filesize.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 check_command() {
-	which "$1" >/dev/null 2>&1
+	command -v "$1" >/dev/null
 }
 
 if check_command gnustat; then
diff --git a/scripts/modules.sh b/scripts/modules.sh
index 4df7e58019f7ee136b73fe6d5f43dfc16cee94db..78b819c34d9c3eace7958d96cb61d9e776aec045 100644
--- a/scripts/modules.sh
+++ b/scripts/modules.sh
@@ -1,6 +1,7 @@
 . ./modules
 [ ! -f "$GLUON_SITEDIR"/modules ] || . "$GLUON_SITEDIR"/modules
 
+# shellcheck disable=SC2086
 FEEDS="$(echo $GLUON_FEEDS $GLUON_SITE_FEEDS | tr ' ' '\n')"
 
 GLUON_MODULES=openwrt
diff --git a/scripts/patch.sh b/scripts/patch.sh
index 082ca1fce2a39072aefd680998752858baf27375..9269ffb51dba3b88f3f5574f5df44892ea2fa26e 100755
--- a/scripts/patch.sh
+++ b/scripts/patch.sh
@@ -3,7 +3,7 @@
 set -e
 shopt -s nullglob
 
-[ "$GLUON_TMPDIR" -a "$GLUON_PATCHESDIR" ] || exit 1
+[ "$GLUON_TMPDIR" ] && [ "$GLUON_PATCHESDIR" ] || exit 1
 
 . scripts/modules.sh
 
diff --git a/scripts/sha256sum.sh b/scripts/sha256sum.sh
index e4f6f3e5b9e8fc532158e28d39c9df3ec6d6e45e..99d97acd0cb57430dec412cf76a0e58c072d531f 100755
--- a/scripts/sha256sum.sh
+++ b/scripts/sha256sum.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 check_command() {
-	which "$1" >/dev/null 2>&1
+	command -v "$1" >/dev/null 2>&1
 }
 
 if check_command sha256sum; then
diff --git a/scripts/sha512sum.sh b/scripts/sha512sum.sh
index d9fd544d504560589f8b01676f78d2ca6eab1b89..cb4b07e4b4b7c2dc8c26d91326dbddb92437c1b7 100755
--- a/scripts/sha512sum.sh
+++ b/scripts/sha512sum.sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 check_command() {
-	which "$1" >/dev/null 2>&1
+	command -v "$1" >/dev/null 2>&1
 }
 
 if check_command sha512sum; then
diff --git a/scripts/update-patches.sh b/scripts/update-patches.sh
index 933d20acddf0e91d4d65ab7d6736ad138f2673cc..94f103fa6bb1aabbafb4fb6453a78a093179ae10 100755
--- a/scripts/update-patches.sh
+++ b/scripts/update-patches.sh
@@ -13,13 +13,13 @@ GLUONDIR="$(pwd)"
 for module in $GLUON_MODULES; do
 	echo "--- Updating patches for module '$module' ---"
 
-	rm -rf "${GLUON_PATCHESDIR}/$module"
+	rm -rf "${GLUON_PATCHESDIR:?}/$module"
 
 	cd "$GLUONDIR"/"$module"
 
 	n=0
 	for commit in $(git rev-list --reverse --no-merges base..patched); do
-		let n=n+1
+		(( ++n ))
 		mkdir -p "${GLUON_PATCHESDIR}/$module"
 		git -c core.abbrev=40 show --pretty=format:'From: %an <%ae>%nDate: %aD%nSubject: %B' --no-renames --binary "$commit" > "${GLUON_PATCHESDIR}/$module/$(printf '%04u' $n)-$(git show -s --pretty=format:%f "$commit").patch"
 	done
diff --git a/scripts/update.sh b/scripts/update.sh
index afea8e712d702e8aa50815ba947339773a26ea9d..0d33a8cc2da25daa71b26261459ff5a99486b3c5 100755
--- a/scripts/update.sh
+++ b/scripts/update.sh
@@ -10,9 +10,9 @@ GLUONDIR="$(pwd)"
 for module in $GLUON_MODULES; do
 	echo "--- Updating module '$module' ---"
 	var=$(echo "$module" | tr '[:lower:]/' '[:upper:]_')
-	eval repo=\${${var}_REPO}
-	eval branch=\${${var}_BRANCH}
-	eval commit=\${${var}_COMMIT}
+	eval 'repo=${'"${var}"'_REPO}'
+	eval 'branch=${'"${var}"'_BRANCH}'
+	eval 'commit=${'"${var}"'_COMMIT}'
 
 	mkdir -p "$GLUONDIR/$module"
 	cd "$GLUONDIR/$module"