Advanced shell Programming Notes (Chapter 31 Gotchas)

Chapter 31 Gotchas

Declare reserved words and characters as variable names

case=value0   #Raise error
23skidoo=value1   #Raise error
#Variable names that begin with numbers are reserved for use by the shell
#Try_ 23skidoo=value1.  Variable names starting with an underscore are allowed

#But It is also not possible to use only underscores as variable names
_=25
echo $_   #$_ Is a special variable that is set as the last parameter of the last command

xyz((!*=value2   #Cause a serious error

Use hyphens or other reserved characters as variables (or function names)

var-1=23   #Replace with "var_1"

function-whatever()   #Use 'function_whatever() 'instead

function.whatever () # The error is replaced by 'functionWhatever()'

Use the same name for variables and functions. This will make the script unable to distinguish between the two

do_something()
{
    echo "This function does something with \"$1\"."
}

do_something=do_something

do_something do_something
#These are legal, but confusing

Improper use of wide white characters. Compared with other programming languages, Bash pays great attention to the use of white space characters

var1 = 23   #'var1=23' is correct

let c = $a -$b   # 'let c=$a-$b' or 'let c=$a-$b' is correct

if [ $a -le 5]   # If [$a - Le 5] is correct
# If ["$a" - Le 5] would be better
# [[$a -le 5]] is also OK

Uninitialized variables are considered NULL, not zero.

#!/bin/bash
echo "uninitialized_var = $uninitialized_var"
#uninitialized_var =

Confuse the = and - EQ operators in the test. Remember: = compares character variables and -eq compares integers.

if [ "$a" = 273 ]   #Is $a an integer or a string?
if [ "$a" -eq 273 ] # If $a is an integer, use this expression

#Sometimes you can mix - eq and = without adverse results
#However

a=273.0   #Not an integer

if [ "$a" = 273 ];then
  echo "Comparative Engineering."
else
  echo "Less effective."
fi

# Same as a = "273" and a="0273"
# Similarly, the problem is still trying to use "- eq" test for non integer values
if [ "$a" -eq 273.0 ];then
 echo "a = $a"
fi   #Interrupt due to error message
# test.sh: [: 273.0: integer expression expected

Misuse of string comparison operator
Example 31-1 number and string comparisons are not equal

#!/bin/bash
#
echo
number=1

while [ "$number" < 5 ]   # Wrong! Should be: while ["$number" - LT 5]
do
  echo -n "$number"
  let "number += 1"
done

echo "----------------------"
while [ "$number" \< 5 ]
do
  echo -n "$number"   # It seems that the ASCII code is right, but it can't work
  let "number += 1"
done

echo;echo "---------------------"

lesser=5
greater=105

if [ "$greater" \< "$lesser" ];then
  echo "$greater is less than $lesser"   #In fact, "105" is less than "5" This is because string comparison is used (in the sort order of ASCII code)
fi
echo
exit 0  

Sometimes the variables in square brackets ([]) need to be referenced (double quotation marks) during testing. If the user does not do so, it may cause unpredictable results. Refer to examples 7-6, 16-5 and 9-6

Commands in the script may fail because the script does not have permission to run. If the user cannot call a command on the command line, even adding the command to a script will fail. At this time, you can try to change the properties of the access command, or even set the suid bit for him (of course, set it as root).

Trying to redirect with - (in fact, it's not an operator) can lead to annoying surprises.

command1 2> - | command2   #Trying to redirect the error of command1 to a pipeline
#Can't work

command1 2>& - |command2   #It didn't work

With Bash version 2 +, you can trigger a repair action when there is an error message.

#!/bin/bash
#
minimun_version=2
E_BAD_VERSION=80

if [ "$BASH_VERSION" \< "$minimun_version" ];then
  echo "This script applies only to Bash edition $minimum Or later"
  echo "Upgrading is highly recommended"
  exit $E_BAD_VERSION
fi
...  

Using bash proprietary features of Bourne shell script (#! / bin/sh) on non Linux machines can cause unpredictable behavior. Linux systems usually alias sh as bash, but this is not necessarily the case in other common UNIX systems.

A script with DOS grid newline character (\ r\n) will fail to execute because #/ Bin / bash \ R \ nnot legal, different from legal #/ bin/bash\n. The solution is to convert the script into a new line character of UNIX grid.

#!/bin/bash
#
echo "Here"
unix2dos $0   #The script changes itself to DOS format first
chmod 755 $0   #Change back to execution permission. ' The UNIX 2dos' command will delete the execution permission

./$0
echo "There"   #The script tries to run itself again But it is a DOS file and will not work properly
exit

shell script to #/ Starting with bin/sh will not run in Bash compatible mode. Some bash specific features may be disabled. Scripts that require full use of bash's proprietary extensions should use #/ Start with bin/bash.

Adding white space characters before the input string at the end of here document in the script will cause unpredictable results.

A script cannot export variables to its parent process or the environment of the parent process. Like the creatures we learn, a child process can inherit from the parent process, but cannot affect the parent process.

WHATEVER=/home/bozo
export WHATEVER
exit 0


bash$ echo $WHATEVER
bash$

You can be sure that back at the command prompt, the $WHATEVER variable is still not set.

Setting and manipulating variables in the subshell and then trying to use the same variables outside the scope of the subshell will lead to unexpected results.

Example 31-2 sub shell defect

#!/bin/bash
#
outer_variable=outer
echo
echo "outer_variable = $outer_variable"
echo
(
#Sub shell start
echo "outer_variable inside subshell = $outer_variable"
inner_variable=inner
echo "inner_variable inside subshell = $inner_variable"
outer_variable=inner
echo "outer_variable inside subshell = $outer_variable"
#export inner_variable
#export outer_variable
)
echo
echo "inner_variable outside subshell = $inner_variable"
echo "outer_variable outside subshell = $outer_variable"
echo
exit 0

Pipelining the echo output to the read command can produce unpredictable results. In this case, read behaves as if it were not in a sub shell. You can use the set command instead.

Example 31-3 pipe the echo output to the read command

#!/bin/bash
#
a=aaa
b=bbb
c=ccc

echo "one two three" | read a b c   #Attempt to reassign a, b, and c
echo
echo "a = $a"    #aaa
echo "b = $b"    #bbb
echo "c = $c"    #ccc
#Reassignment failed

#Use another method below
var=`echo "one two three"`
set -- $var
a=$1;b=$2;c=$3
echo "---------"
echo "a = $a"   # a = one
echo "b = $b"   # b = two
echo "c = $c"   # c = three
#Reassignment succeeded
#-----------------

#Please note that the echo value in the 'read' command works in a sub SHELL Therefore, the value of the variable is changed only in the child SHELL
a=aaa
b=bbb
c=ccc

echo;echo
echo "one two three" | (
    read a b c;
    echo "Inside subshell:";
    echo "a = $a";   #one
    echo "b = $b";   #two
    echo "c = $c"   #three
)
echo "---------------"
echo "Outside subshell: "
echo "a = $a"    #aaa
echo "b = $b"    #bbb
echo "c = $c"    #ccc

exit 0
#Circulating pipeline problem

foundone=false
find $HOME -type f -atime +30 -size 100k |
while true
do
  read f
  echo "$f is over 100KB and has not been accessed in over 30 days"
  echo "Consider moving the file to archives."
  foundone=true
  echo "Subshell level = $BASH_SUBSHELL"   #Subshell level = 1.  Now it is running in the sub shell
done

#The foundone variable always has a false value here, so it is set to true in the child SHELL
if [ $foundone = fales ];then
  echo "No files need archiving."
fi

#  =================Now, use the correct method:===========
foundone=false
for f in $(find $HOME -type f -atime +30 -size 100k)   #No pipes are used
do
  echo "$f is over 100KB and has not been accessed in over 30 days"
  echo "Consider moving the file to archives."
  foundone=true
done

if [ $foundone = fales ];then
  echo "No files need archiving."
fi 

#  =================Another method:===========
#The corresponding part of the read variable value in the script replaces the read variable in the code block, which enables the variable to be shared in the same sub SHELL
find $HOME -type f -atime +30 -size 100k | {
    foundone=false
    while read f
    do
      echo "$f is over 100KB and has not been accessed in over 30 days"
      echo "Consider moving the file to archives."
      foundone=true
    done
    
    if ! $foundone
    then
      echo "No files need archiving."
    fi  
}

The related problem is that a problem occurs when trying to write the output of tail -f to the pipeline and pass it to grep

tail -f /var/log/messages | grep "$ERROR_MSG" >> error.log
# Nothing will be written in the "error.log" file

Using the command "suid" in a script is dangerous because it endangers system security.

It is debatable to write CGI program with Shell. The variable of Shell script is not "type safe", so it will cause undesirable results when used for CGI connection. Secondly, it is difficult to prevent hacker attacks.

Bash does not properly handle double slash (/ /) strings.

Bash scripts written on Linux or BSD may need to be modified so that they can also run on commercial UNIX (or Apple OSX). These scripts often use GNU commands and filtering tools that are more powerful than similar tools on general UNIX systems. An obvious example of this is the text processing tool tr.

Tags: shell

Posted by the apprentice webmaster on Mon, 09 May 2022 02:37:38 +0300