[TOC]
符号
$#
传入脚本的参数个数(不包括argv[0])$?
读取最后执行命令的退出码$$
运行脚本进程的PID$@
所有的位置参数,每个参数由引号引用。$*
所有的位置参数,和$@
区别在于参数不被引号引用。
数组
BASH只支持一维数组,但参数个数没有限制。
声明一个数组:
declare -a array
数组赋值,有多种方法:
array=(var1 var2 var3 ... varN)
array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN)
array[0]=var1
arrya[1]=var2
...
array[n]=varN
计算数组元素个数:${#array[@]}
或者 ${#array[*]}
,BASH的特殊参数 @
和 *
都表示 “扩展位置参数,从1开始”,在数组里可以通用。
提取数组里第n个元素(从0开始): ${array[n]}
提取数组里从下标n开始的m个元素: ${array[@]:n:m}
提取数组里第n个元素的长度: ${#array[n]}
遍历数组:
filename=($(ls))
for var in ${filename[@]}; do
echo $var
done
比如,从“标准输入”读入n次字符串,每次输入的字符串保存在数组array里:
i=0
n=5
while [ "$i" -lt $n ] ; do
echo "Please input strings ... `expr $i + 1`"
read array[$i]
b=${array[$i]}
echo "$b"
i=`expr $i + 1`
done
字符串
可以用符号 ${}
来进行各种字符串操作。
比如,对于这样的字符串 filename=/dir1/dir2/file.txt
提取子串
可以用 ${filename:n:m}
来提取从下标n开始的m个字符。有多少提取多少,否则输出空串,不会报错。
截取子串
可以用 ${}
匹配字符来截取相应的子串。
示例 | 说明 |
---|---|
${filename#*/} |
删除最左边 / 及其左边的字符串:dir1/dir2/file.txt |
${filename##*/} |
删除最右边 / 及其左边的字符串: file.txt |
${filename#/dir1/dir2} |
删除最左边 /dir1/dir2 变成: /file.txt |
${filename##*file} |
删除从最右边 file 及其左边的字符串: .txt |
${filename%/*} |
删除最右边 / 及其右边的字符串: /dir1/dir2 |
${filename%%/*} |
删除最左边 / 及其右边的字符串: (空字符串) |
${filename%dir*} |
删除最右边 dir 及其右边的字符串 /dir1/ |
字符串替换
示例 | 说明 |
---|---|
${filename/dir/folder} |
匹配第一个dir 并替换为folder :/folder1/dir2/file.txt |
${filename//dir/folder} |
匹配所有dir 并替换为folder :/folder1/folder2/file.txt |
${filename/#\/dir1/\/folder1} |
匹配/dir1 开头的字符串并替换为/folder1 :/folder1/dir2/file.txt |
${filename/%.txt/.doc} |
匹配 .txt 结尾的字符串并替换为.doc :/dir1/dir2/file.doc |
区别于工具tr
,后者是集合替换。比如,把字符串里所有的大写字母转换成小写:
echo $filename | tr A-Z a-z
控制结构
if condition
if [ ! -d $DIR -o -f $FILE ]; then
echo "not directory"
elif [ -z $1 ]; then
echo "empty arguments"
else
exit -1
fi
操作符 | 说明 |
---|---|
-a |
逻辑与 |
-d |
判断目录 |
-eq |
俩数值相等 |
-ne |
俩数值不相等, eg: if [ "$UID" -ne "$ROOT_UID" ] |
== |
俩字符串相等 |
!= |
俩字符串不等 |
-f |
判断文件 |
-n |
值非空 |
-o |
逻辑或 |
-z |
值为空 |
switch
case $HOST_TYPE in
"mac")
echo "mac"
;;
"linux")
echo "linux"
;;
*)
echo "unknown"
;;
esac
for loop
for Lang in $(find "$1" -name "*.lproj"); do
echo $Lang
done
while loop
i="0"; time while [ $i -lt 10 ]; do dd if=/dev/zero of=fill bs=1048576 count=64 conv=fdatasync; i=$[$i+1]; done
while true; do dd if=/dev/zero of=fill1 bs=1048576 count=4000 conv=fdatasync; done
输入输出
读文件
比如,hostinfo.txt 里的内容有 host user passwd 三列,有多种读取方法。
可以直接解析到相应变量:
{
while read host user passwd; do
echo $host $user $passwd
done
} < $HOME/hostinfo.txt
或者,逐行读取再做处理:
cat $HOME/hostinfo.txt | while read line; do
echo $line
done
或者,先把文件读到一个数组里,然后再做处理:
hostinfo=($(cat $HOME/hostinfo.txt))
for line in ${hostinfo[@]}; do
echo $line
done
管道
利用有名管道,可以把一堆文本压缩成一个单一的gz文件,并且中间不需要创建临时文件。
$ mkfifo /tmp/foo
$ cat /tmp/foo | gzip > bar.gz
$ for i in $(find ...); do cat $i > /tmp/foo; done
重定向
示例 | 说明 |
---|---|
cmd >&n |
重定向stdout到文件描述符n |
cmd m>&n |
重定向文件描述符m到文件描述符n |
cmd >&- |
关闭stdout |
cmd <&n |
重定向文件描述符n到stdin |
cmd m<&n |
重定向文件描述符n到文件描述符m |
cmd <&- |
关闭stdin |
cmd > file 2>&1 |
重定向stdout和stderr到文件file |
cmd │ tee file |
不影响cmd的IO,同时复制stdout到文件file |