shlint 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +6 -0
- data/lib/checkbashisms +111 -96
- metadata +2 -2
data/README.md
CHANGED
@@ -22,3 +22,9 @@ shells if you're missing any.
|
|
22
22
|
If you're a ruby user, can install using `gem install shlint`
|
23
23
|
|
24
24
|
Any other nix platform, just drop the contents of `lib` into your `$PATH`
|
25
|
+
|
26
|
+
## Resources
|
27
|
+
|
28
|
+
* [Portable Shell Programming](http://www.gnu.org/software/autoconf/manual/autoconf.html#Portable-Shell)
|
29
|
+
* [How to make bash scripts work in dash](http://mywiki.wooledge.org/Bashism)
|
30
|
+
* [POSIX shell specification](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html)
|
data/lib/checkbashisms
CHANGED
@@ -1,18 +1,24 @@
|
|
1
1
|
#! /usr/bin/perl -w
|
2
|
+
|
3
|
+
# This script is essentially copied from /usr/share/lintian/checks/scripts,
|
4
|
+
# which is:
|
5
|
+
# Copyright (C) 1998 Richard Braakman
|
6
|
+
# Copyright (C) 2002 Josip Rodin
|
7
|
+
# This version is
|
8
|
+
# Copyright (C) 2003 Julian Gilbey
|
9
|
+
#
|
10
|
+
# This program is free software; you can redistribute it and/or modify
|
11
|
+
# it under the terms of the GNU General Public License as published by
|
12
|
+
# the Free Software Foundation; either version 2 of the License, or
|
13
|
+
# (at your option) any later version.
|
2
14
|
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# (C) Copyright 1998-2003 Richard Braakman, Josip Rodin and Julian Gilbey
|
9
|
-
# Additional programming by Mark Hobley
|
10
|
-
#
|
11
|
-
# This script is based on source code taken from the lintian project
|
12
|
-
#
|
13
|
-
# This program can be redistributed under the terms of version 2 of the
|
14
|
-
# GNU General Public Licence as published by the Free Software Foundation
|
15
|
+
# This program is distributed in the hope that it will be useful,
|
16
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
+
# GNU General Public License for more details.
|
15
19
|
#
|
20
|
+
# You should have received a copy of the GNU General Public License
|
21
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
22
|
|
17
23
|
use strict;
|
18
24
|
use Getopt::Long;
|
@@ -30,9 +36,13 @@ in /bin/sh scripts.
|
|
30
36
|
EOF
|
31
37
|
|
32
38
|
my $version = <<"EOF";
|
33
|
-
This is $progname version
|
34
|
-
|
35
|
-
|
39
|
+
This is $progname, from the Debian devscripts package, version ###VERSION###
|
40
|
+
This code is copyright 2003 by Julian Gilbey <jdg\@debian.org>,
|
41
|
+
based on original code which is copyright 1998 by Richard Braakman
|
42
|
+
and copyright 2002 by Josip Rodin.
|
43
|
+
This program comes with ABSOLUTELY NO WARRANTY.
|
44
|
+
You are free to redistribute this code under the terms of the
|
45
|
+
GNU General Public License, version 2, or (at your option) any later version.
|
36
46
|
EOF
|
37
47
|
|
38
48
|
my ($opt_echo, $opt_force, $opt_extra, $opt_posix);
|
@@ -60,8 +70,8 @@ $opt_echo = 1 if $opt_posix;
|
|
60
70
|
my $status = 0;
|
61
71
|
my $makefile = 0;
|
62
72
|
my (%bashisms, %string_bashisms, %singlequote_bashisms);
|
63
|
-
my $LEADIN = qr'(?:(?:^|[`&;(|{])\s*|(?:if|then|do|while|shell)\s+)';
|
64
73
|
|
74
|
+
my $LEADIN = qr'(?:(?:^|[`&;(|{])\s*|(?:if|then|do|while|shell)\s+)';
|
65
75
|
init_hashes;
|
66
76
|
|
67
77
|
foreach my $filename (@ARGV) {
|
@@ -81,7 +91,7 @@ foreach my $filename (@ARGV) {
|
|
81
91
|
. "$check_lines_count lines\n";
|
82
92
|
}
|
83
93
|
|
84
|
-
unless (open C, '<',
|
94
|
+
unless (open C, '<', $filename) {
|
85
95
|
warn "cannot open script $filename for reading: $!\n";
|
86
96
|
$status |= 2;
|
87
97
|
next;
|
@@ -95,6 +105,7 @@ foreach my $filename (@ARGV) {
|
|
95
105
|
my $found_rules = 0;
|
96
106
|
my $buffered_orig_line = "";
|
97
107
|
my $buffered_line = "";
|
108
|
+
|
98
109
|
while (<C>) {
|
99
110
|
next unless ($check_lines_count == -1 or $. <= $check_lines_count);
|
100
111
|
|
@@ -111,11 +122,19 @@ foreach my $filename (@ARGV) {
|
|
111
122
|
}
|
112
123
|
next if $opt_force;
|
113
124
|
|
114
|
-
if ($interpreter
|
115
|
-
warn "script $filename
|
125
|
+
if ($interpreter =~ m,/bash$,) {
|
126
|
+
warn "script $filename is already a bash script; skipping\n";
|
127
|
+
$status |= 2;
|
128
|
+
last; # end this file
|
129
|
+
}
|
130
|
+
elsif ($interpreter !~ m,/(sh|posh)$,) {
|
131
|
+
### ksh/zsh?
|
132
|
+
warn "script $filename does not appear to be a /bin/sh script; skipping\n";
|
133
|
+
$status |= 2;
|
134
|
+
last;
|
116
135
|
}
|
117
136
|
} else {
|
118
|
-
warn "script $filename does not appear to have a \#! interpreter line\n";
|
137
|
+
warn "script $filename does not appear to have a \#! interpreter line;\nyou may get strange results\n";
|
119
138
|
}
|
120
139
|
}
|
121
140
|
|
@@ -134,10 +153,10 @@ foreach my $filename (@ARGV) {
|
|
134
153
|
# will be treated as part of the comment.
|
135
154
|
# s/^(?:.*?[^\\])?$quote_string(.*)$/$1/ if $quote_string ne "";
|
136
155
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
156
|
+
# skip comment lines
|
157
|
+
if (m,^\s*\#, && $quote_string eq '' && $buffered_line eq '' && $cat_string eq '') {
|
158
|
+
next;
|
159
|
+
}
|
141
160
|
|
142
161
|
# Remove quoted strings so we can more easily ignore comments
|
143
162
|
# inside them
|
@@ -156,17 +175,17 @@ foreach my $filename (@ARGV) {
|
|
156
175
|
|
157
176
|
# Handle line continuation
|
158
177
|
if (!$makefile && $cat_string eq '' && m/\\$/) {
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
178
|
+
chop;
|
179
|
+
$buffered_line .= $_;
|
180
|
+
$buffered_orig_line .= $orig_line . "\n";
|
181
|
+
next;
|
163
182
|
}
|
164
183
|
|
165
184
|
if ($buffered_line ne '') {
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
185
|
+
$_ = $buffered_line . $_;
|
186
|
+
$orig_line = $buffered_orig_line . $orig_line;
|
187
|
+
$buffered_line ='';
|
188
|
+
$buffered_orig_line ='';
|
170
189
|
}
|
171
190
|
|
172
191
|
if ($makefile) {
|
@@ -184,10 +203,10 @@ foreach my $filename (@ARGV) {
|
|
184
203
|
$_ = $1 if $1;
|
185
204
|
}
|
186
205
|
|
187
|
-
# Fixes for makefiles by Raphael Geissert
|
188
206
|
last if m%^\s*(override\s|export\s)?\s*SHELL\s*:?=\s*(/bin/)?bash\s*%;
|
189
|
-
|
190
|
-
|
207
|
+
|
208
|
+
# Remove "simple" target names
|
209
|
+
s/^[\w%.-]+(?:\s+[\w%.-]+)*::?//;
|
191
210
|
s/^\t//;
|
192
211
|
s/(?<!\$)\$\((\w+)\)/\${$1}/g;
|
193
212
|
s/(\$){2}/$1/g;
|
@@ -259,17 +278,17 @@ foreach my $filename (@ARGV) {
|
|
259
278
|
my $otherquote = ($quote eq "\"" ? "\'" : "\"");
|
260
279
|
|
261
280
|
# Remove balanced quotes and their content
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
281
|
+
$templine =~ s/(^|[^\\\"](?:\\\\)*)\'[^\']*\'/$1/g;
|
282
|
+
$templine =~ s/(^|[^\\\'](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1/g;
|
283
|
+
|
284
|
+
# Don't flag quotes that are themselves quoted
|
285
|
+
# "a'b"
|
286
|
+
$templine =~ s/$otherquote.*?$quote.*?$otherquote//g;
|
287
|
+
# "\""
|
288
|
+
$templine =~ s/(^|[^\\])$quote\\$quote$quote/$1/g;
|
289
|
+
# \' or \"
|
290
|
+
$templine =~ s/\\[\'\"]//g;
|
291
|
+
my $count = () = $templine =~ /(^|(?!\\))$quote/g;
|
273
292
|
|
274
293
|
# If there's an odd number of non-escaped
|
275
294
|
# quotes in the line it's almost certainly the
|
@@ -286,17 +305,17 @@ foreach my $filename (@ARGV) {
|
|
286
305
|
# detect source (.) trying to pass args to the command it runs
|
287
306
|
# The first expression weeds out '. "foo bar"'
|
288
307
|
if (not $found and
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
308
|
+
not m/$LEADIN\.\s+(\"[^\"]+\"|\'[^\']+\'|\$\([^)]+\)+(?:\/[^\s;]+)?)\s*(\&|\||\d?>|<|;|\Z)/
|
309
|
+
and m/$LEADIN(\.\s+[^\s;\`:]+\s+([^\s;]+))/) {
|
310
|
+
if ($2 =~ /^(\&|\||\d?>|<)/) {
|
311
|
+
# everything is ok
|
312
|
+
;
|
313
|
+
} else {
|
314
|
+
$found = 1;
|
315
|
+
$match = $1;
|
316
|
+
$explanation = "sourced script with arguments";
|
317
|
+
output_explanation($filename, $orig_line, $explanation);
|
318
|
+
}
|
300
319
|
}
|
301
320
|
|
302
321
|
# Remove "quoted quotes". They're likely to be inside
|
@@ -362,7 +381,6 @@ foreach my $filename (@ARGV) {
|
|
362
381
|
# double-quoted strings, so now remove those strings as well.
|
363
382
|
$line =~ s/(^|[^\\\'](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g;
|
364
383
|
$cat_line =~ s/(^|[^<\\\'-](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g;
|
365
|
-
|
366
384
|
while (my ($re,$expl) = each %bashisms) {
|
367
385
|
if ($line =~ m/($re)/) {
|
368
386
|
$found = 1;
|
@@ -375,18 +393,19 @@ foreach my $filename (@ARGV) {
|
|
375
393
|
# Only look for the beginning of a heredoc here, after we've
|
376
394
|
# stripped out quoted material, to avoid false positives.
|
377
395
|
if ($cat_line =~ m/(?:^|[^<])\<\<(\-?)\s*(?:[\\]?(\w+)|[\'\"](.*?)[\'\"])/) {
|
378
|
-
|
379
|
-
|
380
|
-
|
396
|
+
$cat_indented = ($1 && $1 eq '-')? 1 : 0;
|
397
|
+
$cat_string = $2;
|
398
|
+
$cat_string = $3 if not defined $cat_string;
|
381
399
|
}
|
382
|
-
|
400
|
+
}
|
383
401
|
}
|
402
|
+
|
384
403
|
warn "error: $filename: Unterminated heredoc found, EOF reached. Wanted: <$cat_string>\n"
|
385
|
-
|
404
|
+
if ($cat_string ne '');
|
386
405
|
warn "error: $filename: Unterminated quoted string found, EOF reached. Wanted: <$quote_string>\n"
|
387
|
-
|
406
|
+
if ($quote_string ne '');
|
388
407
|
warn "error: $filename: EOF reached while on line continuation.\n"
|
389
|
-
|
408
|
+
if ($buffered_line ne '');
|
390
409
|
|
391
410
|
close C;
|
392
411
|
}
|
@@ -463,10 +482,10 @@ sub script_is_evil_and_wrong {
|
|
463
482
|
|
464
483
|
$ret = $. - 1;
|
465
484
|
last;
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
485
|
+
} elsif (m~\@DPATCH\@~) {
|
486
|
+
$ret = $. - 1;
|
487
|
+
last;
|
488
|
+
}
|
470
489
|
|
471
490
|
}
|
472
491
|
close IN;
|
@@ -474,16 +493,15 @@ sub script_is_evil_and_wrong {
|
|
474
493
|
}
|
475
494
|
|
476
495
|
sub init_hashes {
|
477
|
-
my $LEADIN = qr'(?:(^|[`&;(|{])\s*|(if|then|do|while|shell)\s+)';
|
478
496
|
|
479
497
|
%bashisms = (
|
480
498
|
qr'(?:^|\s+)function \w+(\s|\(|\Z)' => q<'function' is useless>,
|
481
|
-
$LEADIN . qr'select\s+\w+' => q<'select' is not
|
499
|
+
$LEADIN . qr'select\s+\w+' => q<'select' is not POSIX>,
|
482
500
|
qr'(test|-o|-a)\s*[^\s]+\s+==\s' => q<should be 'b = a'>,
|
483
501
|
qr'\[\s+[^\]]+\s+==\s' => q<should be 'b = a'>,
|
484
|
-
qr'\s\|\&' => q<pipelining is not
|
502
|
+
qr'\s\|\&' => q<pipelining is not POSIX>,
|
485
503
|
qr'[^\\\$]\{([^\s\\\}]*?,)+[^\\\}\s]*\}' => q<brace expansion>,
|
486
|
-
|
504
|
+
qr'\{\d+\.\.\d+\}' => q<brace expansion, should be $(seq a b)>,
|
487
505
|
qr'(?:^|\s+)\w+\[\d+\]=' => q<bash arrays, H[0]>,
|
488
506
|
$LEADIN . qr'read\s+(?:-[a-qs-zA-Z\d-]+)' => q<read with option other than -r>,
|
489
507
|
$LEADIN . qr'read\s*(?:-\w+\s*)*(?:\".*?\"|[\'].*?[\'])?\s*(?:;|$)'
|
@@ -494,41 +512,36 @@ sub init_hashes {
|
|
494
512
|
qr'(?<![\$\(])\(\(.*\)\)' => q<'((' should be '$(('>,
|
495
513
|
qr'(?:^|\s+)(\[|test)\s+-a' => q<test with unary -a (should be -e)>,
|
496
514
|
qr'\&>' => q<should be \>word 2\>&1>,
|
497
|
-
qr'(<\&|>\&)\s*((-|\d+)[^\s;|)}`&\\\\]|[^-\d\s]+(?<!\$)(?!\d))' =>
|
515
|
+
qr'(<\&|>\&)\s*((-|\d+)[^\s;|)}`&\\\\]|[^-\d\s]+(?<!\$)(?!\d))' =>
|
498
516
|
q<should be \>word 2\>&1>,
|
499
|
-
$LEADIN . qr'kill\s+-[^sl]\w*' => q<kill -[0-9] or -[A-Z]>,
|
500
|
-
$LEADIN . qr'trap\s+["\']?.*["\']?\s+.*[1-9]' => q<trap with signal numbers>,
|
501
|
-
$LEADIN . qr'trap\s+["\']?.*["\']?\s+.*ERR' => q<trap ERR>,
|
502
517
|
qr'\[\[(?!:)' => q<alternative test command ([[ foo ]] should be [ foo ])>,
|
503
518
|
qr'/dev/(tcp|udp)' => q</dev/(tcp|udp)>,
|
504
|
-
$LEADIN . qr'alias\s' => q<alias>,
|
505
|
-
$LEADIN . qr'unalias\s' => q<unalias>,
|
506
519
|
$LEADIN . qr'builtin\s' => q<builtin>,
|
507
520
|
$LEADIN . qr'caller\s' => q<caller>,
|
508
|
-
$LEADIN . qr'complete\s' => q<complete>,
|
509
521
|
$LEADIN . qr'compgen\s' => q<compgen>,
|
522
|
+
$LEADIN . qr'complete\s' => q<complete>,
|
510
523
|
$LEADIN . qr'declare\s' => q<declare>,
|
511
524
|
$LEADIN . qr'dirs(\s|\Z)' => q<dirs>,
|
512
525
|
$LEADIN . qr'disown\s' => q<disown>,
|
513
526
|
$LEADIN . qr'enable\s' => q<enable>,
|
514
|
-
$LEADIN . qr'export\s+-[^p]' => q<export only takes -p as an option>,
|
515
|
-
$LEADIN . qr'export\s+.+=' => q<export foo=bar should be foo=bar; export foo>,
|
516
527
|
$LEADIN . qr'mapfile\s' => q<mapfile>,
|
517
528
|
$LEADIN . qr'readarray\s' => q<readarray>,
|
518
|
-
$LEADIN . qr'readonly\s+-[af]' => q<readonly -[af]>,
|
519
|
-
$LEADIN . qr'(push|pop)d(\s|\Z)' => q<(push|pop)d>,
|
520
|
-
$LEADIN . qr'set\s+-[BHT]+' => q<set -[BHT]>,
|
521
529
|
$LEADIN . qr'shopt(\s|\Z)' => q<shopt>,
|
522
530
|
$LEADIN . qr'suspend\s' => q<suspend>,
|
523
531
|
$LEADIN . qr'time\s' => q<time>,
|
524
532
|
$LEADIN . qr'type\s' => q<type>,
|
525
|
-
|
533
|
+
$LEADIN . qr'typeset\s' => q<typeset>,
|
526
534
|
$LEADIN . qr'ulimit(\s|\Z)' => q<ulimit>,
|
535
|
+
$LEADIN . qr'set\s+-[BHT]+' => q<set -[BHT]>,
|
536
|
+
$LEADIN . qr'alias\s+-p' => q<alias -p>,
|
537
|
+
$LEADIN . qr'unalias\s+-a' => q<unalias -a>,
|
527
538
|
$LEADIN . qr'local\s+-[a-zA-Z]+' => q<local -opt>,
|
528
539
|
qr'(?:^|\s+)\s*\(?\w*[^\(\w\s]+\S*?\s*\(\)\s*([\{|\(]|\Z)'
|
529
540
|
=> q<function names should only contain [a-z0-9_]>,
|
541
|
+
$LEADIN . qr'(push|pop)d(\s|\Z)' => q<(push|pop)d>,
|
542
|
+
$LEADIN . qr'export\s+-[^p]' => q<export only takes -p as an option>,
|
530
543
|
qr'(?:^|\s+)[<>]\(.*?\)' => q<\<() process substituion>,
|
531
|
-
qr'
|
544
|
+
$LEADIN . qr'readonly\s+-[af]' => q<readonly -[af]>,
|
532
545
|
$LEADIN . qr'(sh|\$\{?SHELL\}?) -[rD]' => q<sh -[rD]>,
|
533
546
|
$LEADIN . qr'(sh|\$\{?SHELL\}?) --\w+' => q<sh --long-option>,
|
534
547
|
$LEADIN . qr'(sh|\$\{?SHELL\}?) [-+]O' => q<sh [-+]O>,
|
@@ -536,11 +549,13 @@ sub init_hashes {
|
|
536
549
|
$LEADIN . qr'printf\s+-v' => q<'printf -v var ...' should be var='$(printf ...)'>,
|
537
550
|
$LEADIN . qr'coproc\s' => q<coproc>,
|
538
551
|
qr';;?&' => q<;;& and ;& special case operators>,
|
552
|
+
$LEADIN . qr'jobs\s' => q<jobs>,
|
553
|
+
# $LEADIN . qr'jobs\s+-[^lp]\s' => q<'jobs' with option other than -l or -p>,
|
554
|
+
$LEADIN . qr'command\s+-[^p]\s' => q<'command' with option other than -p>,
|
539
555
|
);
|
540
556
|
|
541
557
|
%string_bashisms = (
|
542
|
-
|
543
|
-
qr'\$\[\w+\]' => q<arithmetic not allowed>,
|
558
|
+
qr'\$\[[^][]+\]' => q<'$[' should be '$(('>,
|
544
559
|
qr'\$\{\w+\:\d+(?::\d+)?\}' => q<${foo:3[:1]}>,
|
545
560
|
qr'\$\{!\w+[\@*]\}' => q<${!prefix[*|@]>,
|
546
561
|
qr'\$\{!\w+\}' => q<${!name}>,
|
@@ -552,27 +567,25 @@ sub init_hashes {
|
|
552
567
|
qr'\$\{?DIRSTACK\}?\b' => q<$DIRSTACK>,
|
553
568
|
qr'\$\{?EUID\}?\b' => q<$EUID should be "$(id -u)">,
|
554
569
|
qr'\$\{?UID\}?\b' => q<$UID should be "$(id -ru)">,
|
555
|
-
qr'\$\{?LINENO\}?\b' => q<$LINENO>,
|
556
570
|
qr'\$\{?SECONDS\}?\b' => q<$SECONDS>,
|
557
571
|
qr'\$\{?BASH_[A-Z]+\}?\b' => q<$BASH_SOMETHING>,
|
558
|
-
qr'\$\{?KSH_[A-Z]+\}?\b' => q<$KSH_SOMETHING>,
|
559
572
|
qr'\$\{?SHELLOPTS\}?\b' => q<$SHELLOPTS>,
|
560
573
|
qr'\$\{?PIPESTATUS\}?\b' => q<$PIPESTATUS>,
|
561
574
|
qr'\$\{?SHLVL\}?\b' => q<$SHLVL>,
|
562
575
|
qr'<<<' => q<\<\<\< here string>,
|
576
|
+
$LEADIN . qr'echo\s+(?:-[^e\s]+\s+)?\"[^\"]*(\\[abcEfnrtv0])+.*?[\"]' => q<unsafe echo with backslash>,
|
563
577
|
qr'\$\(\([\s\w$*/+-]*\w\+\+.*?\)\)' => q<'$((n++))' should be '$n; $((n=n+1))'>,
|
564
578
|
qr'\$\(\([\s\w$*/+-]*\+\+\w.*?\)\)' => q<'$((++n))' should be '$((n=n+1))'>,
|
565
579
|
qr'\$\(\([\s\w$*/+-]*\w\-\-.*?\)\)' => q<'$((n--))' should be '$n; $((n=n-1))'>,
|
566
580
|
qr'\$\(\([\s\w$*/+-]*\-\-\w.*?\)\)' => q<'$((--n))' should be '$((n=n-1))'>,
|
567
|
-
|
568
|
-
$LEADIN . qr'echo\s+(?:-[^e\s]+\s+)?\"[^\"]*(\\[abcEfnrtv0])+.*?[\"]' => q<unsafe echo with backslash>,
|
581
|
+
qr'\$\(\([\s\w$*/+-]*\*\*.*?\)\)' => q<exponentiation is not POSIX>,
|
569
582
|
$LEADIN . qr'printf\s["\'][^"\']+?%[qb].+?["\']' => q<printf %q|%b>,
|
570
583
|
);
|
571
584
|
|
572
585
|
%singlequote_bashisms = (
|
573
586
|
$LEADIN . qr'echo\s+(?:-[^e\s]+\s+)?\'[^\']*(\\[abcEfnrtv0])+.*?[\']' => q<unsafe echo with backslash>,
|
574
|
-
|
575
|
-
|
587
|
+
$LEADIN . qr'source\s+[\"\']?(?:\.\/|\/|\$|[\w~.-])\S*' =>
|
588
|
+
q<should be '.', not 'source'>,
|
576
589
|
);
|
577
590
|
|
578
591
|
if ($opt_echo) {
|
@@ -583,6 +596,8 @@ sub init_hashes {
|
|
583
596
|
$bashisms{$LEADIN . qr'local\s+\w+='} = q<local foo=bar>;
|
584
597
|
$bashisms{$LEADIN . qr'local\s+\w+\s+\w+'} = q<local x y>;
|
585
598
|
$bashisms{$LEADIN . qr'((?:test|\[)\s+.+\s-[ao])\s'} = q<test -a/-o>;
|
599
|
+
$bashisms{$LEADIN . qr'kill\s+-[^sl]\w*'} = q<kill -[0-9] or -[A-Z]>;
|
600
|
+
$bashisms{$LEADIN . qr'trap\s+["\']?.*["\']?\s+.*[1-9]'} = q<trap with signal numbers>;
|
586
601
|
}
|
587
602
|
|
588
603
|
if ($makefile) {
|
@@ -592,7 +607,7 @@ sub init_hashes {
|
|
592
607
|
$bashisms{$LEADIN . qr'\w+\+='} = q<should be VAR="${VAR}foo">;
|
593
608
|
$string_bashisms{qr'(\$\(|\`)\s*\<\s*\S+\s*(\)|\`)'} = q<'$(\< foo)' should be '$(cat foo)'>;
|
594
609
|
}
|
595
|
-
|
610
|
+
|
596
611
|
if ($opt_extra) {
|
597
612
|
$string_bashisms{qr'\$\{?BASH\}?\b'} = q<$BASH>;
|
598
613
|
$string_bashisms{qr'(?:^|\s+)RANDOM='} = q<RANDOM=>;
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shlint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
12
|
+
date: 2012-10-25 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Checks the syntax of your shellscript against known and available shells.
|
15
15
|
email:
|