Bourne shell (/bin/sh) 存在于所有的 Unix 系统上,并且用她写的脚本是(完全)可移植的; `man 1 sh` 是一个好的参考。
## 基础
### 变量和参数
使用 `variable=value` 的命令格式设置变量,其中 variable 是变量名称,value是打算赋给该变量的值。使用 $variable 获取变量值。
MESSAGE="Hello World" # 赋予一个字符串
PI=3.1415 # 赋予一个十进制小数N=8
TWON=`expr $N * 2` # 算术表达式(只限整数)
TWON=$(($N * 2)) # 另一种语法
TWOPI=`echo "$PI * 2" | bc -l` # 使用 bc 进行浮点运算
ZERO=`echo "c($PI/4)-sqrt(2)/2" | bc -l`
命令行参数:
$0, $1, $2, ... # $0 命令本身
$# # 命令参数个数
$* # 所有参数(也可以是 $@)
### 一些特殊的变量
$$ # 当前进程 ID
$? # 最后命令退出状态码
command if [ $? != 0 ]; then
echo "command failed" fimypath=`pwd`mypath=${mypath}/file.txt
echo ${mypath##*/} # 只显示文件名
echo ${mypath%%.*} # 除了扩展名的全路径
var2=${var:=string} # 如果var没有被赋值,则string值先赋值给var,
# 然后再赋值给var2
### 结构控制
for file in `ls`
do
echo $file
done
count=0
while [ $count -lt 5 ]; do
echo $count
sleep 1
count=$(($count + 1))
done
myfunction() {
find . -type f -name "*.$1" -print # $1 为方法的第一个参数
}
myfunction "txt"
#### 产生一个文件
MYHOME=/home/colin
cat > testhome.sh << _EOF# 所有_EOF前的代码都会进入到 testhome.sh 文件中去
if [ -d "$MYHOME" ] ; then
echo $MYHOME exists
else
echo $MYHOME does not exist
fi
_EOF
sh testhome.sh
## Bourne 脚本实例
来一个小实例,此脚本从本 xhtml 文档创建一个 PDF 小册子:
#!/bin/sh# 此脚本可以创建一份供双面打印机打印的 PDF 格式的书
if [ $# -ne 1 ]; then # 检查参数是否等于 1
echo 1>&2 "Usage: $0 HtmlFile"
exit 1 # 如果不等于1,非0退出
fi
file=$1 # 文件变量
fname=${file%.*} # 文件名变量
fext=${file#*.} # 文件扩展名变量
prince $file -o $fname.pdf # www.princexml.com
pdftops -paper A4 -noshrink $fname.pdf $fname.ps # 创建 postscript 小册子
cat $fname.ps |psbook|psnup -Pa4 -2 |pstops -b "2:0,1U(21cm,29.7cm)" > $fname.book.ps
ps2pdf13 -sPAPERSIZE=a4 -sAutoRotatePages=None $fname.book.ps $fname.book.pdf # 在 Windows 上使用 #a4 和 #None!
exit 0 # exit 0 意为成功
## 一些 sed 命令
这里是[单行 sed 命令的金矿](http://student.northpark.edu/pemente/sed/sed1line.txt)。还有一个很好的 [sed 介绍及教程](http://www.grymoire.com/Unix/Sed.html)。
sed 's/string1/string2/g' # 替换 string1 为 string2
sed -i 's/wroong/wrong/g' *.txt # 用 g 替换所有返回的单词
sed 's/\(.*\)1/\12/g' # 修改 anystring1 为 anystring2
sed '/<p>/,/<\/p>/d' t.xhtml # 删除以 <p> 开始,以 </p> 结尾的行
sed '/ *#/d; /^ *$/d' # 删除注释和空行
sed 's/[ \t]*$//' # 删除行尾空格 (使用 tab 代替 \t)
sed 's/^[ \t]*//;s/[ \t]*$//' # 删除行头尾空格
sed 's/[^*]/[&]/' # 括住首字符 [] top -> [t]op
sed = file | sed 'N;s/\n/\t/' > file.num # 为文件添加行号
## 正则表达式
一些基本的正则表达式同样可用于 sed。作为一个良好的启蒙,可看 [基本正则语法](http://www.regular-expressions.info/reference.html)。
[\^$.|?*+() # 特殊字符,其他字符将匹配自身
\ # 转义特殊字符,当成普通字符对待
* # 重复前项 0 次或多次
. # 单个字符除换行符
.* # 匹配 0 个或多个字符
^ # 匹配字符串行开始处
$ # 匹配字符串行结尾处
.$ # 匹配字符串行最后一个字符
^ $ # 匹配单个空格的行
[^A-Z] # 匹配任何以 A-Z 字符开始的行
### 一些实用命令
下列命令对于包含于一个脚本或者单行命令来说很有用。
sort -t. -k1,1n -k2,2n -k3,3n -k4,4n # 排序 IPv4 格式的 IP 地址
echo 'Test' | tr '[:lower:]' '[:upper:]' # 转换成大写
echo foo.bar | cut -d . -f 1 # 返回 foo
PID=$(ps | grep script.sh | grep bin | awk '{print $1}') # 正在运行名为 script 脚本的
PIDPID=$(ps axww | grep [p]ing | awk '{print $1}') # ping 的 PID (w/o grep pid)
IP=$(ifconfig $INTERFACE | sed '/.*inet addr:/!d;s///;s/ .*//') # Linux
IP=$(ifconfig $INTERFACE | sed '/.*inet /!d;s///;s/ .*//') # FreeBSD
if [ `diff file1 file2 | wc -l` != 0 ]; then [...] fi # 文件改变了?
cat /etc/master.passwd | grep -v root | grep -v \*: | awk -F":" \ # 创建 http passwd
'{ printf("%s:%s\n", $1, $2) }' > /usr/local/etc/apache2/passwd
testuser=$(cat /usr/local/etc/apache2/passwd | grep -v \ # 查看 passwd 中的用户
root | grep -v \*: | awk -F":" '{ printf("%s\n", $1) }' | grep ^user$)
:(){ :|:& };: # bash fork 炸弹。会干掉你的机器
tail +2 file > file2 # 删除文件的第一行
我使用一种小伎俩来一次更改许多文件的扩展名。举个例子,从 .cxx 到 .cpp。排除最后的 `| sh` 先测试一下。你同样可以使用命令 `rename` 来做这些,如果安装了的话。或者使用 bash 内建命令。
# ls *.cxx | awk -F. '{print "mv "$0" "$1".cpp"}' | sh
# ls *.c | sed "s/.*/cp & &.$(date "+%Y%m%d")/" | sh # 如 拷贝 *.c 成 *.c.20080401
# rename .cxx .cpp *.cxx # 重命名所有 .cxx 成 .cpp
# for i in *.cxx; do mv $i ${i%%.cxx}.cpp; done # bash 内建的