Shell的知识点整理

这几天抽了点时间看了一下《Advanced Bash-Scripting Guide》, 之前一直觉得shell这门脚本很有意思,虽然用得不多,但感觉shell这门脚本似乎有点粗糙,但看了abs-guide之后就觉得shell还是很强大的,很多高级特性之前都没曾接触过,平时也用得不算很多。浏览了一下abs-guide之后做了一些简单的笔记,方便以后查看,每个”echo ===”之间就是代码片段,在bash version 3.2.48下测试过,点解高亮区的”view source”可以直接复制所有代码。

#--------------- Begin
#!/usr/bin/bash

for arg in "Hello World What The Fuck"
do
    echo $arg
done

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

FILES="/usr/bin/accept
/usr/sbin/pwck
/usr/sbin/chroot
/usr/badblocks
/sbin/ypbind"

for file in $FILES
do
    if [ ! -e "$file" ]
    then
        echo "$file does not exist."; echo
        continue
    fi
    ls -l $file | awk '{ print $8 "     file size:  " $5 }'
done

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

for file in *
do
    echo $file
done

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

NUMBERS="1 3 5 7 9 25.3"

for num in `echo $NUMBERS`
do
    echo -n "$num"
done

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

PASSWD_FILE=/etc/passwd
n=1
for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWD_FILE")
do
    echo "USER #$n = $name"
    let "n += 1"
done

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

echo `seq 10`

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

for ((a=1, b=2; a <= 10; a++, b++))
do
    echo $((a-b))
    echo $[$a-$b]
done

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

ROOT_UID=0
E_NOTROOT=87
E_XCD=86 #can't change directory?

#root check
if [ "$UID" -ne "$ROOT_UID" ]
then
    echo "Must be root to run this script."
    exit $E_NOTROOT
fi
cd /var/log || {
    echo "Cannot change to necessary directory." >&2
    exit $E_XCD;
}

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

let "t2 = ((a = 9, 15 / 3))"

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

# ` -> command substitution
# pdftk is a useful tool for pdf files
`pdftk f1.pdf,f2.pdf cat output combined.pdf`

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

# ${parameter-default}, ${parameter:-default}, return but not set
var1=1
var2=2
#var3 is unset
echo ${var1-$var2}  # 1
echo ${var3-$var2}  # 2
echo "\${var3}=${var3}"      # ${var3}=
# ${parameter=default}, ${parameter:=default}, return and set var default
echo ${var4=$var1}  # 1
echo $var4
echo "\${var4}=${var4}"     # ${var4}=1

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

args=$# # num of args
lastarg=${!args} # or lastarg=${!#}
echo "Last arg is ${lastarg}"
echo "Listing args with \"\$*\":"
for arg in "$*"
do
    echo "Arg #$index = $arg"       # all in one line
    let "index+=1"
done

echo "Listing args with \"\$@\":"
for arg in "$@"
do
    echo "Arg #$index = $arg"       # seperate every arg in new line
    let "index+=1"
done

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

# space should be quoted or escaped
echo {file1,file2}*\ :{\ A," B",' C'}
# file1* : A file1* : B file1* : C file2* : A file2* : B file2* : C

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

echo {a..z}
echo {0..3}

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

# both stdout & stderr
`ls &> a.txt`
# stdout -> stderr
`ls >&2`

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

dt=$(date +%m-%d-%Y)
echo $dt
a=$(ls -l)
echo $a # all in same line
echo "$(ls -l)" # Quoting , don't suppress in one line

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

shift  # shift 3, shift 3 positions
echo $1 # value of $2
echo $2 # nothings echoes, move not copy

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

exit $? # $? reads the exit status of the last command executed

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

if [ 0 ]
then
    echo "0 is true."
else
    echo "0 is false."
fi      # 0 is true.

if [ ]
then
    echo "NULL is true."
else
    echo "NULL is false."
fi      # NULL is false.

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

# page58, if test operators
# if, elif, else
if [ 0 ]
then
    # command
    echo "if statement"
elif [ 1 ]
then
    # command
    echo "elif statement"
else
    # default-command
    echo "else statement"
fi

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

type test
type '['
type '[['

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

# test/[ considers its arguments as comparison expressions or file tests
# and returns an exit status corresponding to the result of the comparison (0 for true, 1 for false)
[ 0 ]
echo $?     # 0/true
[ ]
echo $?     # 1/false

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

# The (( )) construct expands and evaluates an arithmetic expression.
# If the expression evaluates as zero, it returns an exit status of 1, or "false".
# A non-zero expression returns an exit status of 0, or "true".
# This is in marked contrast to using the test and [ ] constructs previously discussed.
((0))
echo $?     # 1/false
(( 5 > 4))
echo $?     # 0/true

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

# A number preceded by a 0 is octal (base 8).
# A number preceded by 0x is hexadecimal (base 16).
# A number with an embedded # evaluates as BASE#NUMBER
a=021 # 8*2+1
b=0x21 # 16*2+1
c=4#21  # 4*2+1

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

# Double-parentheses ((..))  construct is also a mechanism for
# allowing C-style manipulation of variables in Bash, for example, (( var++ )).
(( a = 23 ))
(( a++ ))
(( t = a<45?7:11 ))
let --a

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

ROOTUSER_NAME=root
username=`id -nu`
if [ "$username" = "$ROOTUSER_NAME" ]
then
    echo "Root!"
else
    echo "Not Root!"
fi

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

# $!, PID (process ID) of last job run in background
# Forces completion of an ill-behaved program.
# Useful, for example, in init scripts.
# possibly_hanging_job & { eval 'kill -9 $!' &> /dev/null; }
`wget http://www.facebook.com &`
{ eval 'kill -9 $!' &> /dev/null; echo "kill finished."; }

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

declare -i number=3
declare | grep HOME

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

echo $RANDOM
num=2
echo $((RANDOM % num))

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

# page 115
stringZ=abcABC123ABCabc
echo ${#stringZ}                # 15
echo `expr "$stringZ" : '.*'`

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

var1=unset
previous=$var1
# Four conditions on "while", but only the last one (exit status) controls loop.
while   echo "previous-variable = $previous"
        echo
        previous=$var1
        [ "$var1" != end ]
do
    echo "Input variable #1 (end to exit) "
    read var1
    echo "variable #1 = $var1"
done

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

t=0
condition() {
    ((t++))
    if [ $t -lt 5 ]
    then
        # like exit status
        return 0
    else
        return 1
    fi
}
# condition can be a function
while condition
do
    echo "Still going: t = $t"
done

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

END_CONDITION=end
# loop as long as the condition is false
until [ "$var1" = "$END_CONDITION" ]
do
    echo "Input variable #1 "
    echo "($END_CONDITION to exit)"
    read var1
    echo "variable #1 = $var1"
    echo
done

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

for outer in I II III IV V
do
    echo "$outer "
    for inner in 1 2 3 4 5
    do
        if [ $inner -eq 3 ]
        then
            # break can also optionally take a parameter
            continue 2
        fi
        echo  "$inner "
    done
done # Output : I 1 2 II 1 2 III 1 2 IV 1 2 V 1 2 

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

# case statement
while [ $# -gt 0 ]; do
    case "$1" in
        -d|--debug)
                DEBUG=1
                echo "Debug On."
                ;;
        -c|--conf)
                CONFILE="$2"
                shift
                if [ ! -f $CONFILE]; then
                    echo "Error: Supplied file doesn't exist!"
                    exit $ERROR_OF_FILE_NOT_FOUND
                fi
                ;;
    esac
    shift
done

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

# select statement, "select var [in list]"
# select uses the $PS3 prompt by default
# if [in list] omitted, use cmd line args or func args instead
PS3="Choose your favorite vegetable:"
echo
choice_of() {
    select vegetable
    # [in list] omitted, so 'select' uses arguments passed to function.
    do
        echo
        echo "Your fav veggie is $vegetable."
        echo "Yuck!"
        echo
        break
    done
}
choice_of beans rice carrots radishes tomatoes spinach

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

# cmd substitution, will invoke a subshell
file_listing=`ls -al`
echo $file_listing

file_listing2=$(ls -al)
echo $file_listing2

for arg in `echo a b`; do
    echo $arg
done        # a b in 2 lines

for arg in "`echo a b`"; do
    echo $arg
done        # ab in single line

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

declare -r PI=3.14159265358979
echo $PI
printf "PI is %1.12f\n" $PI
read var1 var2
echo $var1
echo $var2
read
echo "You just input : $REPLY"

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

echo "List of all users:"
OIFS=$IFS;
IFS=:
while read name passwd uid gid fullname ignore
do
    echo "$name ($fullname)"
done < /etc/passwd

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

arr0=( 10 11 12 13 14 15)
arr1=( 20 21 22 23 24 25)
choose_array() {
    eval array_member=\${arr${array_number}[element_number]}
    echo "Element $element_number of arr$array_number is $array_member."
}
array_number=0
element_number=3
choose_array

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

a='$b'
b='$c'
c=d
# each invocation of eval forces a re-evaluation of its arguments
echo $a         # $b
eval echo $a    # $c
eval eval echo $a   # d

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

echo "Positional parameters before set \'uname -a\' :"
echo "Command-line argument #1 = $1"
echo "Command-line argument #2 = $2"
echo "Command-line argument #3 = $3"
# set `Command` can reset the positional parameters that a script
# sees as the result of a command
set `uname -a`
echo "Positional parameters after set \`uname -a\' : "
echo "Field #1 of 'uname -a' = $1"
echo "Field #2 of 'uname -a' = $2"
echo "Field #3 of 'uname -a' = $3"

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

# using set with -- option explicitly assigns the contents of a variable
# to the positional parameters. If no variable follows the --,
# it unsets the positional parameters.
var="one two three four five"
set -- $var
echo $1
echo $2
echo $3

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

usage() {
    echo "Usage:..."
    exit 0
}
flag=false
list=""
set -- $(getopt ahl: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
        -h ) usage;;
        -a ) flag=true;;
        -l ) list="$2"; echo $list; shift;;
        -- ) shift; break;;
        -* ) echo "$0: unrecognized option $1" 1>&2; usage;;
        *  ) break;;
    esac
    shift
done

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

usage() {
    echo "Usage:..."
    exit 0
}
flag=false
list=""
while getopts ":ahl:" Option
do
    case $Option in
        a )
            flag=true;
            echo "Option a : [OPTIND=${OPTIND}]";;
        h )
            echo "Option h : [OPTIND=${OPTIND}]";
            usage;;
        l )
            echo "Option l : [OPTIND=${OPTIND}]";
            list=$OPTARG; echo $list; break;;
        * ) echo "Unrecognized option. $Option";;
    esac
done

echo "$OPTIND"
shift $(($OPTIND -1))
# $1 now references the first non-option item supplied on the command-line
#+ if one exists.
echo $1

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

# same effect as the #include directive in a C program
# var_in_file1=wtf in file1.sh
source file1.sh
echo $var_in_file1

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

# Using the exec builtin, the shell does not fork,
# and the command exec'ed replaces the shell.
exec echo "Exiting \"$0\"."
echo "This echo will never echo."
exit 99

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

# caller is useful in debugging
function2() {
    caller 1
}
function1() {
    function2
    caller 0
}
function1
echo "wtf"
caller 0

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

# true returns a successful(0) exit status
# but does nothing else
# false does the same, but return (1)
true
echo $?     # 0
# endless loop, alias for ":"
while true
do
    echo "wtf"
    sleep 2
    #cmd1
    #cmd2
done

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

# page 204
jobs
disown
bg
fg
wait

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

# external cmd, page 210
`cat`
`tac`
`chattr`
# ...

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

# here documents, simlilar to
# interactive-program < command-file,
# where command-file contains contents
# between "this_is_a_mark_string"
wall <<'this_is_a_mark_string'
Do u know who i am?
this_is_a_mark_string
# - option can supress leading tabs(but not spcaes)
# Quoting or escaping the "limit string" at the head of a here document
# disables parameter substitution within its body
var1=wth
cat <<-MARK_OF_STR
	what the fuck? $var1
MARK_OF_STR

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

# file descriptor
echo 1234567890 > file_name     # write string to "file_name"
exec 3<> file_name              # open "file_name" and assign fd 3 to it
read -n 4 < &3                   # read only 4 chars
# builtin echo do not support -n
printf . >&3                    # write a decimal point there
exec 3>&-                       # close fd 3
cat file_name                   # 1234.67890
# n< &-          close input fd n
# 0<&-, <&-     close stdin
# n>&-          close output fd n
# 1>&-, >&-     close stdout
# child process inhreit open fd, close it if no need

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

# There is no method of 'declaring" the function like C
func1               # error, func1 not defined
declare -f func1    # not help
func1               # still error

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

file=/etc/passwd
pattern=hallo
return_value=""
file_excerpt() {
    echo "Pattern : $1"
    local i=0
    while read line
    do
        echo $line
        ((i++))
        if [ $i -eq 5 ];then
            # custom pre-defined return value
            return_value=(1 2 3)
            # max return value, positive int 255
            return 0
        fi
    done
} < $file

file_excerpt $pattern
echo "Return value is : ${return_value[1]}"

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

# must set this option, else script will not expand aliases
shopt -s expand_aliases
alias llm='ls -al | more'
llm
echo
unalias llm
llm         # error, unalias already

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

# bash permits array operations on variables
# even if the variables are not explicitly declared as arrays.
string=abcd1234
arr_str=("what" "the" "fuck")
echo ${string[*]}           # abcd1234
echo ${arr_str[*]}          # what the fuck
echo ${string[@]}           # abcd1234
echo ${arr_str[@]}          # what the fuck
echo ${#string[@]}          # 1, length
echo ${#arr_str[@]}         # 3

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

# page 416
# many of the standard string operations work on arrays
arrayZ=( one two three four five)
echo ${arrayZ[@]:0}
echo ${arrayZ[@]:1}
echo ${arrayZ[@]:1:3}
echo ${arrayZ[@]/ive/XYZ}
# ...

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

# combine 2 array
arr1=(one two three)
arr2=(four five six)
combined=(${arr1[@]} ${arr2[*]})
echo ${combined[*]}

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

arr1=(
    VAR1=value1
    VAR2=value2
    VAR3=value3
)
arr2=(
    v1="test"
    v2="VAR1=value1 VAR2=value2 VAR3=value3"
    v3=${arr1[*]}
)

test_for_2d_array() {
    t="arr2[*]"
    local ${!t}
    echo "${!t}"        # v1=test v2=VAR1=value1 VAR2=value2 VAR3=value3 v3=VAR1=value1 VAR2=value2 VAR3=value3
    echo "${v1}"        # test
    echo "${v2}"        # VAR1=value1
    echo "${VAR2}"      # value2
    echo "${VAR3}"      # value3
    echo "${v3}"        # VAR1=value1
}

test_for_2d_array

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

# indirect reference
a=b
b=hallo
eval res=\$$a
echo "$res"     # hallo

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

# trap specifies an action on receipt of a signal,
# useful for debugging
trap 'echo "omg"'  EXIT
while true
do
    sleep 2
    echo "hallo"
done

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

# The DEBUG argument to trap causes a specified action to execute
# "after" every command in a script, useful for tracing variables
trap 'echo "TRACE> \$variable = \"$variable\""' DEBUG
variable=29
echo "Just init \$variable to $variable."

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

# trap '' SIGNAL disables SIGNAL for the remainder of the script
# This is useful to protect a critical portion of some uninterruptable actions
trap '' 2       # Signal 2 is Control-C, now disabled
sleep 2
cd
ls
sleep 2
# command...
trap 2          # Reenables Control-C

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

#--------------- End
This entry was posted in Unix/Linux. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>