这几天抽了点时间看了一下《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