shlint 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|