Программирование в BASH

 

sample1.sh

#!/bin/bash

# Proper header for a Bash script.

 

# Cleanup, version 2

 

# Run as root, of course.

# Insert code here to print error message and exit if not root.

 

LOG_DIR=/var/log

# Variables are better than hard-coded values.

cd $LOG_DIR

 

cat /dev/null > messages

cat /dev/null > wtmp

 

echo "Logs cleaned up."

 

exit #  The right and proper method of "exiting" from a script.

     #  A bare "exit" (no parameter) returns the exit status

     #+ of the preceding command.

sample2.sh

#!/bin/bash

# Cleanup, version 3

 

#  Warning:

#  -------

#  This script uses quite a number of features that will be explained

#+ later on.

#  By the time you've finished the first half of the book,

#+ there should be nothing mysterious about it.

 

LOG_DIR=/var/log

ROOT_UID=0     # Only users with $UID 0 have root privileges.

LINES=50       # Default number of lines saved.

E_XCD=86       # Can't change directory?

E_NOTROOT=87   # Non-root exit error.

 

# Run as root, of course.

if [ "$UID" -ne "$ROOT_UID" ]

then

  echo "Must be root to run this script."

  exit $E_NOTROOT

fi  

 

if [ -n "$1" ]

# Test whether command-line argument is present (non-empty).

then

  lines=$1

else  

  lines=$LINES # Default, if not specified on command-line.

fi  

 

cd $LOG_DIR

 

if [ `pwd` != "$LOG_DIR" ]  # or   if [ "$PWD" != "$LOG_DIR" ]

                            # Not in /var/log?

then

  echo "Can't change to $LOG_DIR."

  exit $E_XCD

fi  # Doublecheck if in right directory before messing with log file.

 

# Far more efficient is:

#

# cd /var/log || {

#   echo "Cannot change to necessary directory." >&2

#   exit $E_XCD;

# }

 

tail -n $lines messages > mesg.temp # Save last section of message log file.

mv mesg.temp messages               # Rename it as system log file.

 

#  cat /dev/null > messages

#* No longer needed, as the above method is safer.

 

cat /dev/null > wtmp  #  ': > wtmp' and '> wtmp'  have the same effect.

echo "Log files cleaned up."

#  Note that there are other log files in /var/log not affected

#+ by this script.

 

exit 0

#  A zero return value from the script upon exit indicates success

#+ to the shell.

sample3.sh

#!/bin/bash

# ex9.sh

 

# Variables: assignment and substitution

 

a=375

hello=$a

 

#-------------------------------------------------------------------------

# No space permitted on either side of = sign when initializing variables.

# What happens if there is a space?

 

#  "VARIABLE =value"

#           ^

#% Script tries to run "VARIABLE" command with one argument, "=value".

 

#  "VARIABLE= value"

#            ^

#% Script tries to run "value" command with

#+ the environmental variable "VARIABLE" set to "".

#-------------------------------------------------------------------------

echo hello    # hello

# Not a variable reference, just the string "hello" . . .

 

echo $hello   # 375

#    ^          This *is* a variable reference.

echo ${hello} # 375

# Also a variable reference, as above.

 

# Quoting . . .

echo "$hello"    # 375

echo "${hello}"  # 375

 

echo

 

hello="A B  C   D"

echo $hello   # A B C D

echo "$hello" # A B  C   D

# As you see, echo $hello   and   echo "$hello"   give different results.

# Why?

# =======================================

# Quoting a variable preserves whitespace.

# =======================================

 

echo

 

echo '$hello'  # $hello

#    ^      ^

#  Variable referencing disabled (escaped) by single quotes,

#+ which causes the "$" to be interpreted literally.

 

# Notice the effect of different types of quoting.

 

hello=    # Setting it to a null value.

echo "\$hello (null value) = $hello"

#  Note that setting a variable to a null value is not the same as

#+ unsetting it, although the end result is the same (see below).

 

# --------------------------------------------------------------

 

#  It is permissible to set multiple variables on the same line,

#+ if separated by white space.

#  Caution, this may reduce legibility, and may not be portable.

 

var1=21  var2=22  var3=$V3

echo

echo "var1=$var1   var2=$var2   var3=$var3"

 

# May cause problems with older versions of "sh" . . .

 

# --------------------------------------------------------------

 

echo; echo

 

numbers="one two three"

#           ^   ^

other_numbers="1 2 3"

#               ^ ^

#  If there is whitespace embedded within a variable,

#+ then quotes are necessary.

#  other_numbers=1 2 3                  # Gives an error message.

echo "numbers = $numbers"

echo "other_numbers = $other_numbers"   # other_numbers = 1 2 3

#  Escaping the whitespace also works.

mixed_bag=2\ ---\ Whatever

#           ^    ^ Space after escape (\).

 

echo "$mixed_bag"         # 2 --- Whatever

 

echo; echo

 

echo "uninitialized_variable = $uninitialized_variable"

# Uninitialized variable has null value (no value at all!).

uninitialized_variable=   #  Declaring, but not initializing it --

                          #+ same as setting it to a null value, as above.

echo "uninitialized_variable = $uninitialized_variable"

                          # It still has a null value.

 

uninitialized_variable=23       # Set it.

unset uninitialized_variable    # Unset it.

echo "uninitialized_variable = $uninitialized_variable"

                                # It still has a null value.

echo

 

exit 0

sample4.sh

#!/bin/bash

# Naked variables

 

echo

 

# When is a variable "naked", i.e., lacking the '$' in front?

# When it is being assigned, rather than referenced.

 

# Assignment

a=879

echo "The value of \"a\" is $a."

 

# Assignment using 'let'

let a=16+5

echo "The value of \"a\" is now $a."

 

echo

 

# In a 'for' loop (really, a type of disguised assignment):

echo -n "Values of \"a\" in the loop are: "

for a in 7 8 9 11

do

  echo -n "$a "

done

 

echo

echo

 

# In a 'read' statement (also a type of assignment):

echo -n "Enter \"a\" "

read a

echo "The value of \"a\" is now $a."

 

echo

 

exit 0

sample5.sh

#!/bin/bash

 

a=23              # Simple case

echo $a

b=$a

echo $b

 

# Now, getting a little bit fancier (command substitution).

 

a=`echo Hello!`   # Assigns result of 'echo' command to 'a' ...

echo $a

#  Note that including an exclamation mark (!) within a

#+ command substitution construct will not work from the command-line,

#+ since this triggers the Bash "history mechanism."

#  Inside a script, however, the history functions are disabled.

 

a=`ls -l`         # Assigns result of 'ls -l' command to 'a'

echo $a           # Unquoted, however, it removes tabs and newlines.

echo

echo "$a"         # The quoted variable preserves whitespace.

                  # (See the chapter on "Quoting.")

 

exit 0

sample6.sh

#!/bin/bash

# int-or-string.sh

 

a=2334                   # Integer.

let "a += 1"

echo "a = $a "           # a = 2335

echo                     # Integer, still.

 

 

b=${a/23/BB}             # Substitute "BB" for "23".

                         # This transforms $b into a string.

echo "b = $b"            # b = BB35

declare -i b             # Declaring it an integer doesn't help.

echo "b = $b"            # b = BB35

 

let "b += 1"             # BB35 + 1

echo "b = $b"            # b = 1

echo                     # Bash sets the "integer value" of a string to 0.

 

c=BB34

echo "c = $c"            # c = BB34

d=${c/BB/23}             # Substitute "23" for "BB".

                         # This makes $d an integer.

echo "d = $d"            # d = 2334

let "d += 1"             # 2334 + 1

echo "d = $d"            # d = 2335

echo

 

 

# What about null variables?

e=''                     # ... Or e="" ... Or e=

echo "e = $e"            # e =

let "e += 1"             # Arithmetic operations allowed on a null variable?

echo "e = $e"            # e = 1

echo                     # Null variable transformed into an integer.

 

# What about undeclared variables?

echo "f = $f"            # f =

let "f += 1"             # Arithmetic operations allowed?

echo "f = $f"            # f = 1

echo                     # Undeclared variable transformed into an integer.

#

# However ...

let "f /= $undecl_var"   # Divide by zero?

#   let: f /= : syntax error: operand expected (error token is " ")

# Syntax error! Variable $undecl_var is not set to zero here!

#

# But still ...

let "f /= 0"

#   let: f /= 0: division by 0 (error token is "0")

# Expected behavior.

 

#  Bash (usually) sets the "integer value" of null to zero

#+ when performing an arithmetic operation.

#  But, don't try this at home, folks!

#  It's undocumented and probably non-portable behavior.

 

# Conclusion: Variables in Bash are untyped,

#+ with all attendant consequences.

 

exit $?

sample7.sh

#!/bin/bash

# shft.sh: Using 'shift' to step through all the positional parameters.

 

#  Name this script something like shft.sh,

#+ and invoke it with some parameters.

#+ For example:

#             sh shft.sh a b c def 83 barndoor

 

until [ -z "$1" ]  # Until all parameters used up . . .

do

  echo -n "$1 "

  shift

done

 

echo               # Extra linefeed.

 

# But, what happens to the "used-up" parameters?

echo "$2"

#  Nothing echoes!

#  When $2 shifts into $1 (and there is no $3 to shift into $2)

#+ then $2 remains empty.

#  So, it is not a parameter *copy*, but a *move*.

 

exit

 

#  See also the echo-params.sh script for a "shiftless"

#+ alternative method of stepping through the positional params.

sample8.sh

#!/bin/bash

List="one two three"

 

for a in $List     # Splits the variable in parts at whitespace.

do

  echo "$a"

done

# one

# two

# three

 

echo "---"

 

for a in "$List"   # Preserves whitespace in a single variable.

do #     ^     ^

  echo "$a"

done

# one two three

exit

 

sample9.sh

#!/bin/bash

# escaped.sh: escaped characters

 

#############################################################

### First, let's show some basic escaped-character usage. ###

#############################################################

 

# Escaping a newline.

# ------------------

 

echo ""

 

echo "This will print

as two lines."

# This will print

# as two lines.

 

echo "This will print \

as one line."

# This will print as one line.

 

echo; echo

echo "============="

 

 

echo "\v\v\v\v"      # Prints \v\v\v\v literally.

# Use the -e option with 'echo' to print escaped characters.

echo "============="

echo "VERTICAL TABS"

echo -e "\v\v\v\v"   # Prints 4 vertical tabs.

echo "=============="

 

echo "QUOTATION MARK"

echo -e "\042"       # Prints " (quote, octal ASCII character 42).

echo "=============="

 

# The $'\X' construct makes the -e option unnecessary.

 

echo; echo "NEWLINE and (maybe) BEEP"

echo $'\n'           # Newline.

echo $'\a'           # Alert (beep).

                     # May only flash, not beep, depending on terminal.

 

# We have seen $'\nnn" string expansion, and now . . .

 

# =================================================================== #

# Version 2 of Bash introduced the $'\nnn' string expansion construct.

# =================================================================== #

 

echo "Introducing the \$\' ... \' string-expansion construct . . . "

echo ". . . featuring more quotation marks."

 

echo $'\t \042 \t'   # Quote (") framed by tabs.

# Note that  '\nnn' is an octal value.

 

# It also works with hexadecimal values, in an $'\xhhh' construct.

echo $'\t \x22 \t'  # Quote (") framed by tabs.

# Thank you, Greg Keraunen, for pointing this out.

# Earlier Bash versions allowed '\x022'.

 

echo

 

# Assigning ASCII characters to a variable.

# ----------------------------------------

quote=$'\042'        # " assigned to a variable.

echo "$quote Quoted string $quote and this lies outside the quotes."

 

echo

 

# Concatenating ASCII chars in a variable.

triple_underline=$'\137\137\137'  # 137 is octal ASCII code for '_'.

echo "$triple_underline UNDERLINE $triple_underline"

 

echo

 

ABC=$'\101\102\103\010'           # 101, 102, 103 are octal A, B, C.

echo $ABC

 

echo

escape=$'\033'                    # 033 is octal for escape.

echo "\"escape\" echoes as $escape"

#                                   no visible output.

 

echo

 

exit 0

sample10.sh

#!/bin/bash

 

echo hello

echo $?    # Exit status 0 returned because command executed successfully.

 

lskdf      # Unrecognized command.

echo $?    # Non-zero exit status returned because command failed to execute.

 

echo

 

exit 113   # Will return 113 to shell.

           # To verify this, type "echo $?" after script terminates.

 

#  By convention, an 'exit 0' indicates success,

#+ while a non-zero exit value means an error or anomalous condition.