1#!/usr/bin/perl 2 3# Test t0000..t9999.sh for non portable shell scripts 4# This script can be called with one or more filenames as parameters 5 6use strict; 7use warnings; 8 9my$exit_code=0; 10my%func; 11 12sub err { 13my$msg=shift; 14s/^\s+//; 15s/\s+$//; 16s/\s+/ /g; 17print"$ARGV:$.: error:$msg:$_\n"; 18$exit_code=1; 19} 20 21# glean names of shell functions 22formy$i(@ARGV) { 23open(my$f,'<',$i)or die"$0:$i:$!\n"; 24while(<$f>) { 25$func{$1} =1if/^\s*(\w+)\s*\(\)\s*{\s*$/; 26} 27close$f; 28} 29 30while(<>) { 31chomp; 32# stitch together incomplete lines (those ending with "\") 33while(s/\\$//) { 34$_.=readline; 35chomp; 36} 37 38/\bcp\s+-a/and err 'cp -a is not portable'; 39/\bsed\s+-[^efn]\s+/and err 'sed option not portable (use only -n, -e, -f)'; 40/\becho\s+-[neE]/and err 'echo with option is not portable (use printf)'; 41/^\s*declare\s+/and err 'arrays/declare not portable'; 42/^\s*[^#]\s*which\s/and err 'which is not portable (use type)'; 43/\btest\s+[^=]*==/and err '"test a == b" is not portable (use =)'; 44/\bwc -l.*"\s*=/and err '`"$(wc -l)"` is not portable (use test_line_count)'; 45/\bhead\s+-c\b/and err 'head -c is not portable (use test_copy_bytes BYTES <file >out)'; 46/(?:\$\(seq|^\s*seq\b)/and err 'seq is not portable (use test_seq)'; 47/\bgrep\b.*--file\b/and err 'grep --file FILE is not portable (use grep -f FILE)'; 48/\bexport\s+[A-Za-z0-9_]*=/and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)'; 49/^\s*([A-Z0-9_]+=(\w+|(["']).*?\3)\s+)+(\w+)/and exists($func{$4})and 50 err '"FOO=bar shell_func" assignment extends beyond "shell_func"'; 51# this resets our $. for each file 52close ARGV ifeof; 53} 54exit$exit_code;