-
Notifications
You must be signed in to change notification settings - Fork 0
/
is-valid-shell-script
executable file
·119 lines (105 loc) · 3.57 KB
/
is-valid-shell-script
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
#!/bin/sh
#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Exit on unhandled error exit statuses; turn off filename expansion
# ("globbing"); warn on expansion of unset parameters.
set -efu
PROGNAME=${0##*/}
DIRNAME=${0%/*}
die () {
echo "$PROGNAME: fatal error: $*" >&2
exit 3
}
is_shell_script () {
FILE=$1
# Use parameter expansion tricks as a poor man's pattern matcher; if the
# expansion does not mutate the parameter, then it _didn't_ match the
# pattern.
if [ "${FILE%.bash}" != "$FILE" ] \
|| [ "${FILE%.ksh}" != "$FILE" ] \
|| [ "${FILE%.sh}" != "$FILE" ]
then
# It's claiming to be a Bourne-based script; believe it.
return 0
fi
# If it's executable, let file(1) do the hard work of figuring out what kind
# of program it is.
if [ -x "$FILE" ]
then
DESCRIPTION=$(file -b "$FILE")
case "$DESCRIPTION" in
# Catch POSIX, Korn, Bourne-Again, and Z shell scripts.
("* shell script*"|"* zsh script*")
return 0
;;
# Catch indirections through env. Mind ksh88 and ksh93.
("*env *sh script*"|"*env ksh88 script*"|"*env ksh93 script*")
return 0
;;
esac
fi
# All checks failed; report that the argument is not a shell script.
return 1
}
# Amusingly, this script won't validate itself with "command -v"; the
# "-v" flag was part of the "User Portability Utilities option" in POSIX
# Issue 6 (2004), but only promoted to full mandatory status in Issue 7
# (2017). So instead use "which" (a Debianism, but widely available)
# until Debian's checkbashisms catches up.
#
#if ! command -v checkbashisms > /dev/null
if ! which checkbashisms > /dev/null
then
die "\"checkbashisms\" command not found; is the \"devscripts\" package" \
"installed?"
fi
if ! which file > /dev/null
then
die "\"file\" command not found; is the \"file\" package installed?"
fi
if ! which python3 > /dev/null
then
die "\"python3\" command not found; is the \"python3\" package installed?"
fi
if [ -f .stylefilter ]
then
set -- $(python3 "$DIRNAME"/filter.py -f .stylefilter "$@")
fi
for FILE
do
# Is the current file a shell script? If not, skip it.
is_shell_script "$FILE" || continue
# --force checks for non-POSIX constructs even in scripts that declare bash
# as the interpreter (useful because we don't want any bash scripts).
#
# --posix forces full-POSIX checking, ignoring the couple of exceptions to
# POSIX-compliance permitted by Debian policy.
#
# --extra prints out the offending line after the diagnostic.
if ! checkbashisms --force --posix --extra "$FILE"
then
die "script \"$FILE\" has non-POSIX features"
fi
# Now use the shell's own internal parser to find more subtle problems.
# Even this is not perfect because in this mode, the shell fails to perform
# many expansions due to possible side effects. Also, the shell language is
# not decidable. :-| (See Trienen, Jennerod, "Mining Debian Maintainer
# Scripts",
# https://debconf18.debconf.org/talks/90-mining-debian-maintainer-scripts/ )
#
# Here's an example of a construct sh -n doesn't catch that fails when run
# for real:
#
# [[ a =~ a* ]] || echo a does not equal a
#
# ...and more forgivably, stupid eval tricks like this:
#
# STRING='${ARRAY_DEREF[1]}'; eval echo $STRING
if ! sh -n "$FILE"
then
die "script \"$FILE\" failed syntax check"
fi
done