-
Notifications
You must be signed in to change notification settings - Fork 0
/
bashcheck
executable file
·150 lines (121 loc) · 6.42 KB
/
bashcheck
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env bash
#USAGE: bashcheck filename
#
#EXAMPLE: bashcheck ./bashcheck
#
#DESCRIPTION:
# Does a quick and dirty check of a bash script printing any potentially major
# problems to stdout, specifically it checks for:
# +Assigning values to system variables in an unconstrained way, ie a way
# that will have side effects on the callers environment.
# +Assigning variables or defining functions using Built-in function names
# or keywords.
#
# For more complete and systematic checking, I highly recommend 'shellcheck':
# http://www.shellcheck.net/
# https://github.com/koalaman/shellcheck
#
# Author: Stuart A. Knock
# Originally Written: 2015-11-23
# https://github.com/stuart-knock/bash-tools
#
#TODO: Cleanup output formatting.
#TODO: Consider using bashlogging
###############################################################################
########################## Configurable variables #############################
###############################################################################
#"Basic"|""|"Pernickety"
fussiness="Pernickety"
#The following set of variables define what to check for, you can add or remove
#items from the lists to suit your preferences.
#System variables to check for accidental overwrite
sysvars="auto_resume BASH BASH_ARGC BASH_ARGV BASH_ENV BASH_LINENO BASH_SOURCE
BASH_VERSINFO BASH_VERSION BASH_VERSINFO CDPATH COLUMNS COMP_CWORD
COMP_LINE COMP_POINT COMP_WORDS COMPREPLY CPU DIRSTACK DISPLAY EDITOR
EUID FCEDIT FIGNORE FUNCNAME GLOBIGNORE GROUPS histchars HISTCMD
HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HOME HOST
HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LANGUAGE LC_ALL
LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LESS LINENO LINES LOGNAME
LS_COLORS MACHTYPE MAIL MAILCHECK MAILPATH MORE null OLDPWD OPTARG
OPTERR OPTIND OSTYPE PAGER PATH PIPESTATUS POSIXLY_CORRECT PPID
PROMPT_COMMAND PS1 PS2 PS4 PS3 PWD RANDOM REPLY SECONDS SHELL
SHELLOPTS SHLVL TERM TIMEFORMAT TMOUT TMPDIR UID USER USERNAME
XAUTHORITY"
#Built in and "standard" functions to check for overwrite or reassignment
builtins="agrep awk bash bind break caller case cat cd chmod chown command cp
curl cut declare df dirs do done du echo egrep enable esac eval exec
exit export false fi find for function getopts grep gzip hash head
help id if in info let line ln local logout ls mapfile mkdir mv mysql
popd print printf ps pushd pwd read readarray readonly return rm sed
shift shopt sleep sort source su sudo tail tee test then tr true type
typeset ulimit unalias uniq unset wait wc while who whoami xargs"
#TODO: switch to using bashlogging
#If "Enabled", display extra information for debugging purposes.
DEBUGMODE=""
###############################################################################
############################## Define Functions ###############################
##############################################################################
#TODO: Can't figure out how to prevent expansion of ${FUNCNAME} but still have
# it properly expand inside the function when used as a printf format.
#ferrfmt='ERROR: ${FUNCNAME}(): %s\n'
#TODO: Allow for input variable, possibly a file or two, that adds variable and
# function names to check just for this run. Useful for large projects where you
# want to make sure no core variables or functions are being clobbered by other
# parts of the script.
# Check a Bash script for variable definitions that may clobber system variables
check_assignment(){
[[ "$DEBUGMODE" = "Enabled" ]] && printf '%s\n' "ENTERING: ${FUNCNAME}()"
local errfmt="ERROR: ${FUNCNAME}(): %s\n"
[[ "$#" != '1' ]] && { printf "$errfmt" "only supports one argument"; return 1; }
local chkscrpt="$1"
[[ -f "$chkscrpt" ]] || { printf "$errfmt" "can't find script: $chkscrpt"; return 1; }
printf "Checking sysvars for possible overwriting:\n"
#Convert sysvars variable into a regex.
local overwritevars=$(printf "("$(printf '%s|' $sysvars)')=' | sed s/'|)='/')='/)
#Match whole words followed immediately by "="
grep --colour -nwE $overwritevars "$chkscrpt"
if [[ "$?" != 0 ]]; then echo "No problems found"; else printf '\n'; fi
#TODO: Exclude cases where the occurrence is commented or proceeded by local
# or a similar construct that protects the global variable.
if [[ "$fussiness" == "Pernickety" ]] ; then
#While assigning a variable with an existing function name shouldn't cause
#any problem, it could make things confusing, so if we're being extra fussy
#we should avoid it.
printf "Checking builtins for possible overwriting:\n"
#Convert builtins variable into a regex.
local overwritevars=$(printf "("$(printf '%s|' $builtins)')=' | sed s/'|)='/')='/)
#Match whole words followed immediately by "="
grep --colour -nwE $overwritevars "$chkscrpt"
if [[ "$?" != 0 ]]; then echo " No problems found"; else printf '\n'; fi
fi
return 0
}
# Check a Bash script for value setting or function definitions using common
# function names or keywords, eg test, exit, local done
check_redefinition(){
[[ "$DEBUGMODE" = "Enabled" ]] && printf '%s\n' "ENTERING: ${FUNCNAME}()"
local errfmt="ERROR: ${FUNCNAME}(): %s\n"
[[ "$#" != '1' ]] && { printf "$errfmt" "only supports one argument\n"; return 1; }
local chkscrpt="$1"
[[ -f "$chkscrpt" ]] || { printf "$errfmt" "can't find script: $chkscrpt\n"; return 1; }
printf "Checking builtins for possible redefinition:\n"
#Convert sysvars variable into a regex.
local redeffuncs=$(printf "("$(printf '%s|' $builtins)')\s*\(\s*\)\s*\{' | sed s/'|)'/')'/)
#Match whole words followed immediately by "(){", allowing for spaces before
grep --colour -nwE $redeffuncs "$chkscrpt"
if [[ "$?" != 0 ]]; then echo " No problems found"; else printf '\n'; fi
#TODO: Exclude cases where the occurrence is commented...
return 0
}
###############################################################################
################################ Main Script ##################################
###############################################################################
# make sure filename supplied as command line arg else die
[[ "$#" != '1' ]] && { printf " Usage: $0 filename\n"; exit 1; }
filename="$1"
printf "Running bashcheck on file: $filename\n"
check_assignment "$filename"
retCA="$?"
check_redefinition "$filename"
retCR="$?"
exit $((retCA||retCR))