Learning_the_bash_Shell_Third_Edition 10/n

Flow Control

 Exit Status

Every UNIX command, whether it comes from source code in C, some other language, or a shell script/function, returns an integer code to its calling process—the shell in this case—when it finishes. This is called the exit status. 0 is usually the OK exit status, while anything else (1 to 255) usually denotes an error.

 

pushd ( )
{
dirname=$1
DIR_STACK="$dirname ${DIR_STACK:-$PWD' '}"
cd ${dirname:?"missing directory name."}
echo $DIR_STACK
}

  

This function requires a valid directory as its argument. Let’s look at how it handles error conditions: if no argument is given, the third line of code prints an error message and exits. This is fine.

However, the function reacts deceptively when an argument is given that isn’t a valid directory. In case you didn’t figure it out when reading the last chapter, here is what happens: the cd fails, leaving you in the same directory you were in. This is also appropriate. But the second line of code has pushed the bad directory onto the stack anyway, and the last line prints a message that leads you to believe that the push was successful. Even placing the cd before the stack assignment won’t help because it doesn’t exit the function if there is an error.

 

We need to prevent the bad directory from being pushed and to print an error message. Here is how we can do this:

 

pushd ( )
{
dirname=$1
if cd ${dirname:?"missing directory name."}
# if cd was successful
then
DIR_STACK="$dirname ${DIR_STACK:-$PWD' '}" # push the directory
echo $DIR_STACK
else
echo still in $PWD.
# else do nothing
fi
}

Let’s say you have the following code in your .bash_profile.

cd ( )
{
builtin cd "$@"
echo "$OLDPWD —> $PWD"
}

  

The builtin command allows us to do this. builtin tells the shell to use the built-in command and ignore any function of that name. Using builtin is easy; you just give it the name of the built-in you want to execute and any parameters you want to pass. If you pass in the name of something which isn’t a built-in command, builtin will display an appropriate message. For example: builtin: alice: not a shell builtin.

 

We want this function to return the same exit status that the built-in cd returns. The problem is that the exit status is reset by every command, so it “disappears” if you don’t save it immediately. In this function, the built-in cd’s exit status disappears when the echo statement runs (and sets its own exit status).

 

Therefore, we need to save the status that cd sets and use it as the entire function’s exit status. Two shell features we haven’t seen yet provide the way. First is the special shell variable ?, whose value ($?) is the exit status of the last command that ran.

For example:

 

So, to save the exit status we need to assign the value of ? to a variable with the line es=$? right after the cd is done.

 Return

return can only be used inside functions, and shell scripts that have been executed with source. In contrast, the statement exit N exits the entire script, no matter how deeply you are

nested in functions.

 

cd ( )
{
builtin cd "$@"
es=$?
echo "$OLDPWD —> $PWD"
return $es
}

  Combinations of Exit Statuses

  

statement1 && statement2

The syntax statement1 && statement2 means, “execute statement1, and if its exit status is 0, execute statement2.”

tatement1 || statement2 

 

The syntax statement1 || statement2 is the converse: it means, “execute statement1, and if its exit status is not 0, execute statement2.”

Condition Tests

 

[ condition ]

  String comparisons

Operator

True if...

str1 = str2

str1 matches str2

str1 != str2

str1 does not match str2

str1 < str2

str1 is less than str2

str1 > str2

str1 is greater than str2

-n str1

str1 is not null (has length greater than 0)

-z str1

str1 is null (has length 0)

 

 

 File attribute checking

Operator

True if...

-a file

file exists

-d file

file exists and is a directory

-e file

file exists; same as -a

-f file

file exists and is a regular file (i.e., not a directory or other special type of file)

-r file

You have read permission on file

-s file

file exists and is not empty

-w file

You have write permission on file

-x file

You have execute permission on file, or directory search permission if it is a directory

-N file

file was modified since it was last read

-O file

You own file

-G file

file’s group ID matches yours (or one of yours, if you are in multiple groups)

file1 -nt file2

file1 is newer than file2

file1 -ot file2

file1 is older than file2

 

You can also negate the truth value of a conditional expression by preceding it with an exclamation point (!), so that ! expr evaluates to true only if expr is false. Furthermore, you can make complex logical expressions of conditional operators by grouping them with parentheses (which must be “escaped” with backslashes to prevent the shell from treating them specially), and by using two logical operators we haven’t seen yet: -a (AND) and -o (OR).

The -a and -o operators are similar to the && and || operators used with exit statuses. However, unlike those operators, -a and -o are only available inside a test conditional expression.

 

Here is how we would use two of the file operators, a logical operator, and a string operator to fix the problem of duplicate stack entries in our pushd function. Instead of having cd determine whether the argument given is a valid directory—i.e., by returning with a bad exit status if it’s not—we can do the checking ourselves. Here is the code:

 

pushd ( )
{
dirname=$1
if [ -n "$dirname" ] && [ \( -d "$dirname" \) -a \
\( -x "$dirname" \) ]; then
DIR_STACK="$dirname ${DIR_STACK:-$PWD' '}"
cd $dirname
echo "$DIR_STACK"
else
echo "still in $PWD."
fi
}

  

The conditional expression evaluates to true only if the argument $1 is not null (-n), a directory (-d) and the user has permission to change to it (-x). * Notice that this conditional handles the case where the argument is missing ($dirname is null) first; if it is,the rest of the condition is not executed. This is important because, if we had just put:

 

if [ \( -n "$dirname"\) -a \( -d "$dirname" \) -a \
\( -x "$dirname" \) ]; then

Task 5-1

Write a script that prints essentially the same information as ls -l but in a more user friendly way.

 

 

#

if [ ! -e "$1" ]; then
echo "file $1 does not exist."
exit 1
fi
if [ -d "$1" ]; then
echo -n "$1 is a directory that you may "
if [ ! -x "$1" ]; then
echo -n "not "
fi
echo "search."
elif [ -f "$1" ]; then
echo "$1 is a regular file."
else
echo "$1 is a special type of file."
fi
if [ -O "$1" ]; then
echo 'you own the file.'
else
echo 'you do not own the file.'
fi
if [ -r "$1" ]; then
echo 'you have read permission on the file.'
fi
if [ -w "$1" ]; then
echo 'you have write permission on the file.'
fi
if [ -x "$1" -a ! -d "$1" ]; then
echo 'you have execute permission on the file.'
fi

  

 

posted @ 2021-03-04 10:54  碧水东流至此回  阅读(58)  评论(0编辑  收藏  举报