1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
8
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
11
TITLE="Advanced Bash-Scripting Guide"
12
HREF="index.html"><LINK
14
TITLE="Advanced Topics"
15
HREF="part5.html"><LINK
18
HREF="options.html"><LINK
20
TITLE="Scripting With Style"
21
HREF="scrstyle.html"><META
22
HTTP-EQUIV="Content-Style-Type"
23
CONTENT="text/css"><LINK
25
HREF="common/kde-common.css"
27
HTTP-EQUIV="Content-Type"
28
CONTENT="text/html; charset=iso-8859-1"><META
29
HTTP-EQUIV="Content-Language"
32
HREF="common/kde-localised.css"
34
TITLE="KDE-English"><LINK
36
HREF="common/kde-default.css"
38
TITLE="KDE-Default"></HEAD
46
STYLE="font-family: sans-serif;"
50
SUMMARY="Header navigation table"
59
>Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting</TH
96
>Chapter 33. Gotchas</H1
115
CLASS="FOREIGNPHRASE"
116
>Gli enigmi sono tre, la morte
123
CLASS="FOREIGNPHRASE"
124
>No, no! Gli enigmi sono tre, una la
142
>Here are some (non-recommended!) scripting practices that
143
will bring excitement into an otherwise dull life.</P
152
>Assigning reserved words or characters to variable names.</P
161
CLASS="PROGRAMLISTING"
162
> 1 case=value0 # Causes problems.
163
2 23skidoo=value1 # Also problems.
164
3 # Variable names starting with a digit are reserved by the shell.
165
4 # Try _23skidoo=value1. Starting variables with an underscore is okay.
167
6 # However . . . using just an underscore will not work.
169
8 echo $_ # $_ is a special variable set to last arg of last command.
170
9 # But . . . _ is a valid function name!
172
11 xyz((!*=value2 # Causes severe problems.
173
12 # As of version 3 of Bash, periods are not allowed within variable names.</PRE
182
>Using a hyphen or other reserved characters in a variable name (or
192
CLASS="PROGRAMLISTING"
194
2 # Use 'var_1' instead.
196
4 function-whatever () # Error
197
5 # Use 'function_whatever ()' instead.
200
8 # As of version 3 of Bash, periods are not allowed within function names.
201
9 function.whatever () # Error
202
10 # Use 'functionWhatever ()' instead.</PRE
211
>Using the same name for a variable and a function. This can make a
212
script difficult to understand.</P
221
CLASS="PROGRAMLISTING"
222
> 1 do_something ()
224
3 echo "This function does something with \"$1\"."
227
6 do_something=do_something
229
8 do_something do_something
231
10 # All this is legal, but highly confusing.</PRE
244
HREF="special-chars.html#WHITESPACEREF"
247
In contrast to other programming languages, Bash can be quite
248
finicky about whitespace.</P
257
CLASS="PROGRAMLISTING"
258
> 1 var1 = 23 # 'var1=23' is correct.
259
2 # On line above, Bash attempts to execute command "var1"
260
3 # with the arguments "=" and "23".
262
5 let c = $a - $b # Instead: let c=$a-$b or let "c = $a - $b"
264
7 if [ $a -le 5] # if [ $a -le 5 ] is correct.
265
8 # ^^ if [ "$a" -le 5 ] is even better.
266
9 # [[ $a -le 5 ]] also works.</PRE
280
>Not terminating with a <A
281
HREF="special-chars.html#SEMICOLONREF"
285
HREF="special-chars.html#CODEBLOCKREF"
286
>code block within curly
297
CLASS="PROGRAMLISTING"
298
> 1 { ls -l; df; echo "Done." }
299
2 # bash: syntax error: unexpected end of file
301
4 { ls -l; df; echo "Done."; }
302
5 # ^ ### Final command needs semicolon.</PRE
316
> Assuming uninitialized variables (variables before a value is
317
assigned to them) are <SPAN
321
uninitialized variable has a value of <I
340
CLASS="PROGRAMLISTING"
343
3 echo "uninitialized_var = $uninitialized_var"
344
4 # uninitialized_var =</PRE
366
> in a test. Remember,
370
> is for comparing literal variables
383
CLASS="PROGRAMLISTING"
384
> 1 if [ "$a" = 273 ] # Is $a an integer or string?
385
2 if [ "$a" -eq 273 ] # If $a is an integer.
387
4 # Sometimes you can interchange -eq and = without adverse consequences.
388
5 # However . . .
391
8 a=273.0 # Not an integer.
393
10 if [ "$a" = 273 ]
395
12 echo "Comparison works."
397
14 echo "Comparison does not work."
398
15 fi # Comparison does not work.
400
17 # Same with a=" 273" and a="0273".
403
20 # Likewise, problems trying to use "-eq" with non-integer values.
405
22 if [ "$a" -eq 273.0 ]
407
24 echo "a = $a"
408
25 fi # Aborts with an error message.
409
26 # test.sh: [: 273.0: integer expression expected</PRE
424
HREF="comparison-ops.html#SCOMPARISON1"
425
>string comparison</A
435
>Example 33-1. Numerical and string comparison are not equivalent</B
444
CLASS="PROGRAMLISTING"
446
2 # bad-op.sh: Trying to use a string comparison on integers.
451
7 # The following while-loop has two errors:
452
8 #+ one blatant, and the other subtle.
454
10 while [ "$number" < 5 ] # Wrong! Should be: while [ "$number" -lt 5 ]
456
12 echo -n "$number "
457
13 let "number += 1"
459
15 # Attempt to run this bombs with the error message:
460
16 #+ bad-op.sh: line 10: 5: No such file or directory
461
17 # Within single brackets, "<" must be escaped,
462
18 #+ and even then, it's still wrong for comparing integers.
464
20 echo "---------------------"
466
22 while [ "$number" \< 5 ] # 1 2 3 4
468
24 echo -n "$number " # It *seems* to work, but . . .
469
25 let "number += 1" #+ it actually does an ASCII comparison,
470
26 done #+ rather than a numerical one.
472
28 echo; echo "---------------------"
474
30 # This can cause problems. For example:
479
35 if [ "$greater" \< "$lesser" ]
481
37 echo "$greater is less than $lesser"
482
38 fi # 105 is less than 5
483
39 # In fact, "105" actually is less than "5"
484
40 #+ in a string comparison (ASCII sort order).
501
>Attempting to use <A
502
HREF="internal.html#LETREF"
505
to set string variables.</P
514
CLASS="PROGRAMLISTING"
515
> 1 let "a = hello, you"
516
2 echo "$a" # 0</PRE
529
>Sometimes variables within <SPAN
533
([ ]) need to be quoted (double quotes). Failure to do so may
534
cause unexpected behavior. See <A
535
HREF="comparison-ops.html#STRTEST"
538
HREF="redircb.html#REDIR2"
541
HREF="variables2.html#ARGLIST"
552
>Quoting a variable containing whitespace <A
553
HREF="quoting.html#WSQUO"
554
>prevents splitting</A
557
HREF="quoting.html#VARSPLITTING"
569
>Commands issued from a script may fail to execute because
570
the script owner lacks execute permission for them. If a user
571
cannot invoke a command from the command-line, then putting it
572
into a script will likewise fail. Try changing the attributes of
573
the command in question, perhaps even setting the suid bit
586
>Attempting to use <B
590
operator (which it is not) will usually result in an unpleasant
600
CLASS="PROGRAMLISTING"
601
> 1 command1 2> - | command2
602
2 # Trying to redirect error output of command1 into a pipe . . .
603
3 # . . . will not work.
605
5 command1 2>& - | command2 # Also futile.
607
7 Thanks, S.C.</PRE
621
HREF="bash2.html#BASH2REF"
624
functionality may cause a bailout with error messages. Older
625
Linux machines may have version 1.XX of Bash as the default
635
CLASS="PROGRAMLISTING"
638
3 minimum_version=2
639
4 # Since Chet Ramey is constantly adding features to Bash,
640
5 # you may set $minimum_version to 2.XX, 3.XX, or whatever is appropriate.
641
6 E_BAD_VERSION=80
643
8 if [ "$BASH_VERSION" \< "$minimum_version" ]
645
10 echo "This script works only with Bash, version $minimum or greater."
646
11 echo "Upgrade strongly recommended."
647
12 exit $E_BAD_VERSION
658
>Using Bash-specific functionality in a <A
659
HREF="why-shell.html#BASHDEF"
667
>) on a non-Linux machine
669
HREF="gotchas.html#BINSH"
670
>may cause unexpected behavior</A
672
A Linux system usually aliases <B
679
>, but this does not necessarily hold true
680
for a generic UNIX machine.</P
689
>Using undocumented features in Bash turns out to be a
690
dangerous practice. In previous releases of this
691
book there were several scripts that depended on the
695
> that, although the maximum value
697
HREF="exit-status.html#EXITSTATUSREF"
700
HREF="functions.html#RETURNREF"
702
> value was 255, that limit
703
did not apply to <SPAN
710
Unfortunately, in version 2.05b and later, that loophole
712
HREF="functions.html#RETURNTEST"
723
> A script with DOS-type newlines (<TT
729
will fail to execute, since <TT
748
the same as the expected <TT
754
fix is to convert the script to UNIX-style newlines.</P
763
CLASS="PROGRAMLISTING"
768
5 unix2dos $0 # Script changes itself to DOS format.
769
6 chmod 755 $0 # Change back to execute permission.
770
7 # The 'unix2dos' command removes execute permission.
772
9 ./$0 # Script tries to run itself again.
773
10 # But it won't work as a DOS file.
791
>A shell script headed by <TT
797
will not run in full Bash-compatibility mode. Some Bash-specific
798
functions might be disabled. Scripts that need complete
799
access to all the Bash-specific extensions should start with
810
HREF="here-docs.html#INDENTEDLS"
811
>Putting whitespace in front of
812
the terminating limit string</A
814
HREF="here-docs.html#HEREDOCREF"
816
> will cause unexpected
817
behavior in a script.</P
824
>Putting more than one
828
> statement in a function <A
829
HREF="assortedtips.html#RVT"
830
>whose output is captured</A
839
CLASS="PROGRAMLISTING"
842
3 echo "Whatever ... " # Delete this line!
843
4 let "retval = $1 + $2"
849
10 echo "Sum of $num1 and $num2 = $(add2 $num1 $num2)"
851
12 # Sum of 12 and 43 = Whatever ...
854
15 # The "echoes" concatenate.</PRE
860
HREF="assortedtips.html#RVTCAUTION"
867
NAME="PARCHILDPROBREF"
876
HREF="internal.html#FORKREF"
879
or to the environment. Just as we learned in biology, a child
880
process can inherit from a parent, but not vice versa.</P
889
CLASS="PROGRAMLISTING"
890
> 1 WHATEVER=/home/bozo
891
2 export WHATEVER
913
CLASS="COMPUTEROUTPUT"
926
> Sure enough, back at the command prompt, $WHATEVER remains unset.
936
>Setting and manipulating variables in a <A
937
HREF="subshells.html#SUBSHELLSREF"
940
to use those same variables outside the scope of the subshell will
941
result an unpleasant surprise.</P
949
>Example 33-2. Subshell Pitfalls</B
958
CLASS="PROGRAMLISTING"
960
2 # Pitfalls of variables in a subshell.
962
4 outer_variable=outer
964
6 echo "outer_variable = $outer_variable"
968
10 # Begin subshell
970
12 echo "outer_variable inside subshell = $outer_variable"
971
13 inner_variable=inner # Set
972
14 echo "inner_variable inside subshell = $inner_variable"
973
15 outer_variable=inner # Will value change globally?
974
16 echo "outer_variable inside subshell = $outer_variable"
976
18 # Will 'exporting' make a difference?
977
19 # export inner_variable
978
20 # export outer_variable
979
21 # Try it and see.
981
23 # End subshell
985
27 echo "inner_variable outside subshell = $inner_variable" # Unset.
986
28 echo "outer_variable outside subshell = $outer_variable" # Unchanged.
991
33 # What happens if you uncomment lines 19 and 20?
992
34 # Does it make a difference?</PRE
1006
HREF="special-chars.html#PIPEREF"
1013
HREF="internal.html#READREF"
1015
> may produce unexpected
1016
results. In this scenario, the <B
1020
acts as if it were running in a subshell. Instead, use
1022
HREF="internal.html#SETREF"
1025
HREF="internal.html#SETPOS"
1035
>Example 33-3. Piping the output of <I
1051
CLASS="PROGRAMLISTING"
1052
> 1 #!/bin/bash
1053
2 # badread.sh:
1054
3 # Attempting to use 'echo and 'read'
1055
4 #+ to assign variables non-interactively.
1061
10 echo "one two three" | read a b c
1062
11 # Try to reassign a, b, and c.
1065
14 echo "a = $a" # a = aaa
1066
15 echo "b = $b" # b = bbb
1067
16 echo "c = $c" # c = ccc
1068
17 # Reassignment failed.
1070
19 # ------------------------------
1072
21 # Try the following alternative.
1074
23 var=`echo "one two three"`
1076
25 a=$1; b=$2; c=$3
1078
27 echo "-------"
1079
28 echo "a = $a" # a = one
1080
29 echo "b = $b" # b = two
1081
30 echo "c = $c" # c = three
1082
31 # Reassignment succeeded.
1084
33 # ------------------------------
1086
35 # Note also that an echo to a 'read' works within a subshell.
1087
36 # However, the value of the variable changes *only* within the subshell.
1089
38 a=aaa # Starting all over again.
1094
43 echo "one two three" | ( read a b c;
1095
44 echo "Inside subshell: "; echo "a = $a"; echo "b = $b"; echo "c = $c" )
1099
48 echo "-----------------"
1100
49 echo "Outside subshell: "
1101
50 echo "a = $a" # a = aaa
1102
51 echo "b = $b" # b = bbb
1103
52 echo "c = $c" # c = ccc
1117
>In fact, as Anthony Richardson points out, piping to
1124
> loop can cause a similar problem.</P
1134
CLASS="PROGRAMLISTING"
1135
> 1 # Loop piping troubles.
1136
2 # This example by Anthony Richardson,
1137
3 #+ with addendum by Wilbert Berendsen.
1140
6 foundone=false
1141
7 find $HOME -type f -atime +30 -size 100k |
1145
11 echo "$f is over 100KB and has not been accessed in over 30 days"
1146
12 echo "Consider moving the file to archives."
1147
13 foundone=true
1148
14 # ------------------------------------
1149
15 echo "Subshell level = $BASH_SUBSHELL"
1150
16 # Subshell level = 1
1151
17 # Yes, we're inside a subshell.
1152
18 # ------------------------------------
1155
21 # foundone will always be false here since it is
1156
22 #+ set to true inside a subshell
1157
23 if [ $foundone = false ]
1159
25 echo "No files need archiving."
1162
28 # =====================Now, here is the correct way:=================
1164
30 foundone=false
1165
31 for f in $(find $HOME -type f -atime +30 -size 100k) # No pipe here.
1167
33 echo "$f is over 100KB and has not been accessed in over 30 days"
1168
34 echo "Consider moving the file to archives."
1169
35 foundone=true
1172
38 if [ $foundone = false ]
1174
40 echo "No files need archiving."
1177
43 # ==================And here is another alternative==================
1179
45 # Places the part of the script that reads the variables
1180
46 #+ within a code block, so they share the same subshell.
1181
47 # Thank you, W.B.
1183
49 find $HOME -type f -atime +30 -size 100k | {
1184
50 foundone=false
1185
51 while read f
1187
53 echo "$f is over 100KB and has not been accessed in over 30 days"
1188
54 echo "Consider moving the file to archives."
1189
55 foundone=true
1192
58 if ! $foundone
1194
60 echo "No files need archiving."
1208
> A lookalike problem occurs when trying to write the
1217
HREF="textproc.html#GREPREF"
1227
CLASS="PROGRAMLISTING"
1228
> 1 tail -f /var/log/messages | grep "$ERROR_MSG" >> error.log
1229
2 # The "error.log" file will not have anything written to it.
1230
3 # As Samuli Kaipiainen points out, this results from grep
1231
4 #+ buffering its output.
1232
5 # The fix is to add the "--line-buffered" parameter to grep.</PRE
1249
> commands within scripts is risky,
1250
as it may compromise system security.
1253
HREF="#FTN.AEN19504"
1265
>Using shell scripts for CGI programming may be problematic. Shell
1266
script variables are not <SPAN
1269
> and this can cause
1270
undesirable behavior as far as CGI is concerned. Moreover, it is
1273
>"cracker-proof"</SPAN
1278
>Bash does not handle the <A
1279
HREF="internal.html#DOUBLESLASHREF"
1294
>Bash scripts written for Linux or BSD systems may need
1295
fixups to run on a commercial UNIX (or Apple OSX) machine. Such
1296
scripts often employ the GNU set of commands and filters,
1297
which have greater functionality than their generic UNIX
1298
counterparts. This is particularly true of such text processing
1300
HREF="textproc.html#TRREF"
1322
>Danger is near thee --</I
1326
>Beware, beware, beware, beware.</I
1330
>Many brave hearts are asleep in the deep.</I
1342
>--A.J. Lamb and H.W. Petrie</I
1363
HREF="gotchas.html#AEN19504"
1372
HREF="fto.html#SUIDREF"
1375
permission on the script itself has no effect in Linux
1376
and most other UNIX flavors.</P
1385
SUMMARY="Footer navigation table"
1414
HREF="scrstyle.html"
1438
>Scripting With Style</TD
b'\\ No newline at end of file'