ShellCheck


finds bugs in your shell scripts.
You can cabal, apt, dnf, pkg or brew install it locally right now.
Paste a script to try it out:
📄
Your Editor (Ace – loading 800kb of JS)
 
#!/bin/sh

export ONLINE=1
export QUIET=0
export SSLCURL=1
export DAYOFWEEK=$(date +"%u")
export DISTRIB=0
export IPADDR="10.42.2.1"
export PFDIR="/usr/local/etc/dnsmasq.d"
export TMPDIR="/tmp"
export domains="${PFDIR}/pfb_dnsbl.conf"
export domainspaused="${PFDIR}/pfb_dnsbl.zzz"
export tmpdomains="${TMPDIR}/pfb_dnsbl.tmp"
export hosts="${PFDIR}/pfb_hosts.conf"
export hostspaused="${PFDIR}/pfb_hosts.zzz"
export tmphosts="${TMPDIR}/pfb_hosts.tmp"
export pauseflag="${PFDIR}/PAUSED"
export blacklist="${PFDIR}/blacklist.txt"
export whitelist="${PFDIR}/whitelist.txt"
export base64wl="${PFDIR}/whitelist64.txt"
export myBlacklist="${PFDIR}/myBlacklist.txt"
export myWhitelist="${PFDIR}/myWhitelist.txt"
export PFLOG="${PFDIR}/pfblocked.log"
export SHELL=/bin/sh
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin:"${PFDIR}"
export PWD="${PFDIR}"
LC_ALL=C
export LC_ALL

###############################################################################

cd "${PFDIR}" || exit
logger ">>> $(basename "$0") started"

###############################################################################

# cURL certificates and options
if [ -z "$(which curl)" ]; then
	echo ">>> WARNING: cURL not found"
	echo ">>> ERROR: ABORTING"
	exit 1
fi

export CURL_CA_BUNDLE="${PFDIR}/cacert.pem"

GET() { 
	curl -fsk "$@"; 
}
GETSSL() { 
	curl -fsk "$@"; 
}

[ $SSLCURL -eq 1 ] && unset -f GETSSL && GETSSL() { curl -fsk --capath "$PFDIR" --cacert "$CURL_CA_BUNDLE" "$@"; }


SEDCLEAN () {
	sed -r 's/^[[:blank:]]*//; s/[[:blank:]]*$//; s/^[[:punct:]]*//; s/[[:punct:]]*$//; /^$/d; /^\s*$/d;' "$@"
}


GREPFILTER() { 
	grep -o '^[^#]*' | grep -vF -e "::" -e ";" -e "//" -e "http" -e "https" -e "@" -e "mailto" | tr -cd "\000-\177"; 
}

MKSERV() { 
	sed -r 's/.*/server=\/&\//'; 
}
MKADDR() { 
	sed -r "s/.*/address=\/&\/$IPADDR/"; 
}


# echo & log
logFile ()
{
	[ $QUIET -eq 0 ] && echo "$1"
	echo "$1" >> $PFLOG
}

# print file size
printFileSize ()
{
	logFile "# Size of $1: $(du -h "$1" | awk '{print $1}')"
}

# restart dnsmasq
restart_dnsmasq ()
{
	logger ">>> $(basename "$0") restarting dnsmasq"
	pfSsh.php playback svc restart dnsmasq &&
	logger ">>> $(basename "$0") restarted dnsmasq"
}

# resume protection
protectOn ()
{
	if [ -f $pauseflag ] && { [ -f $domainspaused ] || [ -f $hostspaused ]; }; then
		logFile ">>> RESUMING PROTECTION"
		mv $domainspaused $domains
		mv $hostspaused $hosts
		rm -f $pauseflag
		restart_dnsmasq
	fi
	logger ">>> $(basename "$0") finished"
	exit 0
}

# pause protection
protectOff ()
{
	logFile ">>> WARNING: PAUSING PROTECTION"
	[ -f $domains ] && mv $domains $domainspaused
	[ -f $hosts ] && mv $hosts $hostspaused
	echo "" > $domains
	echo "" > $hosts
	echo "PAUSED" > $pauseflag
	restart_dnsmasq
	logFile ">>> Type $(basename "$0") --resume to resume protection."
	logger ">>> $(basename "$0") finished"
	exit 0
}

# print help options

printHelp () {
	local name=$(basename "$0")
	cat <<USAGE
USAGE:
	$name [-? | -h | --help] [-v | --version] [-b | --bl=<domain.name>] [-w | --wl=<domain.name>] ...

OPERATION:
	[-d | -D]			Ignore myBlacklist/myWhitelist entries
	[-b | --bl=]	domain.name	Add domain.name to myBlacklist
	[-w | --wl=]	domain.name	Add domain.name to myWhitelist
	[-i | --ip=]	ip.ad.dr.ss	Send ads to this IP, default: $IPADDR
	[-q | --quiet]			Print outout to log file only
	[-p | --pause]			Pause protection
	[-r | --resume]			Resume protection
	[-s | --secure]			Use SSL/TLS certs for secure file transfer
	[-o | --offline]		Process local lists without downloading
	[-h | --help]			Display this help screen and exit
	[-v | --version]		Print $name version and exit

EXAMPLES:
	$name -s2 --ip=172.31.255.254 --bl=example1.com --wl=example2.com
	$name -3Fqs -b example1.com -w example2.com --wl=example3.com

USAGE
	logger ">>> $name finished"
	exit 0
}

###############################################################################

# process command line arguments
while getopts "h?vdDpPqQrRsSoOb:w:i:-:" opt; do
	case ${opt} in
		h|\? ) printHelp ;;
		d|D  ) DISTRIB=1 ;;
		q|Q  ) QUIET=1 ;;
		p|P  ) protectOff ;;
		r|R  ) protectOn ;;
		s|S  ) SSLCURL=1 ;;
		o|O  ) ONLINE=0 ;;
		b    ) echo "$OPTARG" >> $myBlacklist ;;
		w    ) echo "$OPTARG" >> $myWhitelist ;;
		i    ) IPADDR="$OPTARG" ;;
		-    ) LONG_OPTARG="${OPTARG#*=}"
		case $OPTARG in
			bl=?*   ) ARG_BL="$LONG_OPTARG" ; echo "$ARG_BL" >> $myBlacklist ;;
			bl*     ) echo ">>> ERROR: no arguments for --$OPTARG option" >&2; exit 2 ;;
			wl=?*   ) ARG_WL="$LONG_OPTARG" ; echo "$ARG_WL" >> $myWhitelist ;;
			wl*     ) echo ">>> ERROR: no arguments for --$OPTARG option" >&2; exit 2 ;;
			ip=?*   ) ARG_IP="$LONG_OPTARG" ; IPADDR=$ARG_IP ;;
			ip*     ) echo ">>> ERROR: no arguments for --$OPTARG option" >&2; exit 2 ;;
			quiet   ) QUIET=1 ;;
			pause   ) protectOff ;;
			resume  ) protectOn ;;
			secure  ) SSLCURL=1 ;;
			offline ) ONLINE=0 ;;
			help    ) printHelp ;;
			quiet* | pause* | resume* | secure* | offline* | help* )
					echo ">>> ERROR: no arguments allowed for --$OPTARG option" >&2; exit 2 ;;
			'' )    break ;; # "--" terminates argument processing
			* )     echo ">>> ERROR: unsupported option --$OPTARG" >&2; exit 2 ;;
		esac ;;
  	  \? ) exit 2 ;;  # getopts already reported the illegal option
	esac
done

shift $((OPTIND-1)) # remove parsed options and args from $@ list

###############################################################################

# display banner
TIMERSTART=$(date +%s)
logFile "======================================================"
logFile "|	      $(date)	     |"
logFile "======================================================"

###############################################################################

# force resume if user forgets to turn it back on
if [ -f $pauseflag ] && { [ -f $domainspaused ] || [ -f $hostspaused ]; }; then
	logFile "# USER FORGOT TO RESUME PROTECTION AFTER PAUSING"
	protectOn
fi

###############################################################################

# if internet is accessible, download files
if ping -q -c 1 -W 1 google.com &> /dev/null; then

	logFile "# NETWORK: UP | MODE: ONLINE"
	logFile "# IP ADDRESS FOR ADS: $IPADDR"
	logFile "# SECURE [0=NO | 1=YES]: $SSLCURL"

	if [ ! -s cacert.pem ] || { [ "${DAYOFWEEK}" -eq 1 ] || [ "${DAYOFWEEK}" -eq 4 ]; }; then
		logFile "> Downloading / updating cURL certificates"
		GETSSL --remote-name --time-cond cacert.pem https://curl.haxx.se/ca/cacert.pem
	fi

	logFile "# Creating Domains"

        logFile "> Processing SANYALnet Domain Blacklist"
	GETSSL http://sanyalnet-cloud-vps.freeddns.org/adblocklist.conf | awk -F/ '{print $2}' | GREPFILTER > $tmpdomains
	GETSSL http://sanyalnet-cloud-vps.freeddns.org/adblocklist.conf | awk -F/ '{print $2}' | GREPFILTER >> $tmpdomains


	logFile "# Creating Host file"

	logFile "> Processing TSPPRS blocklists"
	GETSSL https://tspprs.com/dl/cl1 | GREPFILTER >> $tmphosts


	logFile "> Processing CHEF-KOCH lists"
	GETSSL https://raw.githubusercontent.com/CHEF-KOCH/WebRTC-tracking/master/WebRTC.txt | GREPFILTER | awk '{print $2}' >> $tmphosts
	GETSSL https://raw.githubusercontent.com/CHEF-KOCH/Spotify-Ad-free/master/Spotifynulled.txt | GREPFILTER | awk '{print $2}' >> $tmphosts
	GETSSL https://raw.githubusercontent.com/CHEF-KOCH/Audio-fingerprint-pages/master/AudioFp.txt | GREPFILTER | awk '{print $2}' >> $tmphosts
	GETSSL https://raw.githubusercontent.com/CHEF-KOCH/Canvas-fingerprinting-pages/master/Canvas.txt | GREPFILTER | awk '{print $2}' >> $tmphosts
	GETSSL https://raw.githubusercontent.com/CHEF-KOCH/Canvas-Font-Fingerprinting-pages/master/Canvas.txt | GREPFILTER | awk '{print $2}' >> $tmphosts


	logFile "> Updating official blacklist/whitelist files"
	GETSSL https://jasonhill.co.uk/pfsense/ytadblock.txt | GREPFILTER > $blacklist
	GETSSL https://raw.githubusercontent.com/ookangzheng/blahdns/master/hosts/whitelist.txt | GREPFILTER > $whitelist
	GETSSL https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/optional-list.txt | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/referral-sites.txt | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/raghavdua1995/DNSlock-PiHole-whitelist/master/whitelist.list | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/deathbybandaid/piholeparser/master/Subscribable-Lists/CombinedWhitelists/CombinedWhiteLists.txt | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/tankmohit/UnifiedHosts/master/whitelist | GREPFILTER >> $whitelist
	GETSSL https://raw.githubusercontent.com/m-parashar/adbhostgen/master/whitelists/fruitydomains > $base64wl
	uudecode $base64wl < applewhitelist >> $whitelist && rm applewhitelist && rm $base64wl

else
	logFile "# NETWORK: DOWN | MODE: OFFLINE"
	logger ">>> $(basename "$0") finished"
	exit 0
fi

if [ $ONLINE -eq 0 ]; then
	logFile "# NETWORK: DOWN | MODE: OFFLINE"
	logFile "# OFFLINE PROCESSING"
	[[ -s $domains ]] && mv $domains $tmpdomains
	[[ -s $hosts ]] && awk '{ print $2 }' "$hosts" > "$tmphosts"
	restart_dnsmasq
	logger ">>> $(basename "$0") finished"
	exit 0
fi

###############################################################################

# calculate and print file sizes
printFileSize $tmpdomains
printFileSize $tmphosts

# remove duplicates and extra whitespace, sort alphabetically
logFile "> Processing blacklist/whitelist files"
[[ -s $blacklist ]] && SEDCLEAN | sort -u < tmpbl > "$blacklist"
[[ -s $whitelist ]] && SEDCLEAN | sort -u < tmpwl > "$whitelist"



# if not building for distribution, process myBlacklist and myWhitelist files
# remove duplicates and extra whitespace, sort alphabetically
# and allow users' myBlacklist precedence over defaults
if [ $DISTRIB -eq 0 ] && { [ -s "$myBlacklist" ] || [ -s "$myWhitelist" ]; }; then
	logFile "> Processing myBlacklist/myWhitelist files"
	[[ -s $myBlacklist ]] && SEDCLEAN "$myBlacklist" | sort -u < tmpmybl > "$myBlacklist"
	[[ -s $myWhitelist ]] && SEDCLEAN "$myWhitelist" | sort -u < tmpmywl > "$myWhitelist"
	cat $blacklist < $myBlacklist - > tmpbl
	cat $whitelist < $myWhitelist - | grep -Fvwf $myBlacklist > tmpwl
fi

# trim leading and trailig whitespace, delete all blank lines including the ones with whitespace
# remove non-printable non-ASCII characters
# merge blacklists with other lists and remove whitelist entries from the stream
logFile "> Processing final Domain blocklist"
[[ -s $tmpdomains ]] && SEDCLEAN | grep -Fvwf tmpwl | sort -u | MKSERV < tmpmybl > "$domains"
logFile "> Processing final host file"
[[ -s $tmphosts ]] && SEDCLEAN | grep -Fvwf tmpwl | sort -u | MKSERV < tmpmybl > "$hosts"

logFile "> Removing temporary files"
rm -f $tmpdomains
rm -f $tmphosts
rm -f tmpbl
rm -f tmpwl

# calculate and print file sizes
printFileSize $domains
printFileSize $hosts

# Count how many pfhosts/whitelists were added so it can be displayed to the user
numDomains=$( read -r numDomains _ < <(wc -l "$domains") | sed 's/^[ \t]*//')
logFile "# Number of Domains blocked: approx $numDomains"
numHosts=$( read -r numHosts _ < <(wc -l "$hosts") | sed 's/^[ \t]*//')
logFile "# Number of Hosts blocked: approx $numHosts"

logFile "> Restarting DNS Forwarder"
restart_dnsmasq

TIMERSTOP=$(date +%s)
RTMINUTES=$(( $((TIMERSTOP - TIMERSTART)) /60 ))
RTSECONDS=$(( $((TIMERSTOP - TIMERSTART)) %60 ))
logFile "# Total time: $RTMINUTES:$RTSECONDS minutes"
logFile "# DONE"
logger ">>> $(basename "$0") finished"
exit 0
# FIN
📄
ShellCheck Output
If you paste a script in the editor above, this window will show shellcheck output.

ShellCheck is...

A special thanks to: GitpodBashSupport Pro Route4MeSiemensper1234cavcrosbydcminterphotostructureCronitorsteve-chavezChrLaucjgibsonCelebian LLC.org loves open source BestKru

Wiki Sitemap