http://hi.baidu.com/smilewwh/blog/item/1d81d9190783f179dbb4bd14.html
shell编程小记
2007年11月27日 星期二 16:44
sh bash csh tcsh ksh
cat /etc/shells 可查看已知的shell
/bin/dash 更符合标准,向前兼容
/bin/sh->bash 执行脚本快,但交互弱些
脚本的执行方法:
1.sh ./script.sh
2.chmod +x script.sh; ./script.sh
脚本开头表明解释器:
#!/bin/sh
#!/usr/bin/sed
#!/usr/bin/perl
#!/usr/bin/python
非交互式的shell就是脚本,交互式的就是应答式的。
脚本执行用的机制是fork-and-exec,内建命令执行不需要fork新的进程
shell的内建命令可以用man bash-buildins查看,用which命令查不到的是内建命令
$ cd ..; ls
$ (cd ..; ls) 这不会改变当前路径,因为带()会创建子进程来运行
关于bash启动文件:
一.交互式意味着可以输入命令:
登录shell就是系统验证完用户名和密码后得到的shell
启动读取/etc/profile ~/.bash_profile or ~/.bash_login or ~/.profile or /etc/bash.bashrc
非登录shell就是通过图标或菜单打开一个终端,如xterm,得到的shell
启动读取~/.bashrc
二.非交互式是为执行脚本而fork出来的shell
启动文件1.由变量BASH_ENV定义 2.以sh命令调用,读取的文件是/etc/profile或~/.profile
变量的类型:
环境变量->可以由当前shell传递给子进程使用,env或printenv可以查看,常用的有PATH,PWD,USER,SHELL等
本地变量->只存在于当前shell,使用内建命令set可显示所有变量(含环境变量)和函数
设置变量:
VARNAME="value" 本地变量
export VARNAME="value" 设为环境变量,子shell可以使用
shell中变量无需要特别声明,直接使用就可以了.??
删除变量: 使用内建命令unset VARNAME
读取变量:
$echo $VAR
$echo $VARabc
$echo ${VAR}abc {}用于这种场合
代换substitution
文件名代换globbing: * ? []
$ls /dev/sda*
$ls tw?.doc
$ls tw[0-9].doc 在shell中完成替换,再传到命令ls
反引号backquote命令代换:
DATE=`date` 表示内部是条命令,而不是一个字串或变量
$echo $(date) $()里面是命令,与`date`同
算术代换$((expression))
expression当中只能有 + - * / 和 () ,只能做整数运算
引用字符->用来去除字符或字的特殊含义
/ 可用来去除单个字符的特殊意义
' 可用来保持' '内所有字符的字面值
" 可用来保持" "内所有字符的字面值( 其实与不加引号情况同),下列情况除外:
如:$ echo $'a/tb' 结果显示:$ a b
进程IO:
0表示标准输入,1表示标准输出,2表示标准错误输出
可以通过> < | 改变默认的输入输出方向
如:command > /dev/null 2>&1
/dev/null用于吸收数据并抛弃, 标准输出已经被改变了,因此&1已经指向了/dev/null,无任何信息输出
bash脚本编程:
参数与特殊变量:
$0 所执行脚本的路径名或函数名
$n n>0,表示第n个参数
$# 参数的个数
$* 所有参数一起括起来组成"$1 $2 ... $n"
$@ 所有参数单独括起来组成"$1" "$2" ... "$n"
$? 上一条命令的退出状态
$$ 当前shell的进程号
$! 上一条后台命令产生的进程的进程号
set用于把一个串作为当前脚本的参数,可用$n等进行提取,如
set $(date)
echo month is $2
unset命令用于删除变量,如
foo="hello"
unset foo
内建命令shift用于移动参数序列,如
shift 2 后原来的$3成为了$1
eval 可将显示的内容当命令执行,如
echo hello > out
eval echo hello > out
函数:函数在被使用后保留,除非在他们被使用后进行unset,set可显示,which可显示;
function FUNCTION { COMMANDS; }
或
FUNCTION () { COMMANDS; }
FUNCTION abc 引用时,函数名后面跟的就是传递给函数的参数
函数调用可以传递参数,函数内可以使用$n等系列字符引用,函数内可设置变量,用内建命令local可设置局部变量,函数返回用return value
shell的结束用exit value返回值给父进程,如exit 1表示错误,成功时返回0;
if条件判断表达式,正确返回0:
[ -d FILE ] 判断是否为目录
[ -f FILE ] 判断是否文件存在
[ FILE1 -nt FILE2 ] 1比2新或1在2不在,为真 ot 相反 ef 相同的设备和节点号,为真
[ STRING1 == STRING2 ] 字符串比较,!=, <, >
[ ARG1 OP ARG2 ] 数字比较,OP为 -eq 等于, -ne不等, -lt小于, -le小等, -gt大于, -ge大等
[ ! EXPR ]
[ ( EXPR ) ]
[ EXPR1 -a EXPR2 ] 表示and
[ EXPR1 -o EXPR2 ] 表示or
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
if TEST1; then
CONSE1
elseif TEST2; then
CONSE2
esle CONSE3;
fi
其中空格不能少, ; 可以用回车取代; 若then 或 else 后面没有要执行的语句,则用 then : 或 else :
case EXPRESSION in
CASE1) COMMAND-LIST;;
CASE2) COMMAND-LIST;;
...
CASEn) COMMAND-LIST;;
esac
for NAME in LIST; do COMMANDS; done
while CONTROL-COMMAND; do CONSEQUENT-COMMANDS; done
经典:
LOOP=1
while read LINE
do
echo "$LOOP: $LINE"
LOOP=`expr $LOOP + 1 `
done < $FILENAME 可以从文件读入行,并显示行号
mkdir $DIRECTORY > /dev/null 2>&1 可以创建目录,并不显示任何信息
test COND1 && COMMANDS short circuit 前面的条件成立,才执行后面
[ COND1 ] || COMMANDS test与[ ]作用相同,前面的条件不成立,才执行后面
基于这个逻辑false && * = false true || * = true
stop)
echo -n "shutting down"
kill `cat /var/run/sshd2_$PORT.pid` 直接获取cat出来的pid,让kill使用
echo "done."
;;
restart)
$0 stop 直接调用shell本身作为命令
$0 start
;;
一些实例:
#!/bin/sh
ls -la
pwd
exit 0
#!/bin/sh
echo "Is it morning? Please answer yes or n"
read timeofday
case "$timeofday" in
yes | y | Yes | YES)
echo "Good morning"
echo "Up bright and early this morning"
;;
[nN]*)
echo "Good afternoon"
;;
*)
echo "Sorry, answer not recognized"
echo "Please answer yes or no"
exit 1
;;
esac
exit 0
#!/bin/sh
OUTPUT="> out.file"
echo hello $OUTPUT
OUTPUT="> out.file"
eval echo hello $OUTPUT
#!/bin/sh
for foo in bar fud 43
do
echo $foo
done
for i in 1 2 3
do
echo $(($i+$i))
done
exit 0
#!/bin/sh
yes_or_no()
{
echo "Is your name $* ?" $*是传进函数的参数
while true 确保不输入或输入错误还有机会输入
do
echo -n "Enter yes or no: " -n表示不换行
read x
case "$x" in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo "Answer yes or no" 给输入错误用while提供足够机会
esac
done
}
#the main part of the program begins:
echo " Original parameters are $*"
if yes_or_no "$1" 把$1作为参数传给函数yes_or_no
then
echo "Hi $1, nice name"
else
echo "Never mind"
fi
exit 0
#!/bin/sh
if test -f /bin/bash;then echo "file /bin/bash exists";fi ;分割符号也可用回车代替
if [ -d /bin/bash ];then echo "/bin/bash is a directory"
else echo "/bin/bash is NOT a directory";fi
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ];then echo "Good morning"
else echo "Good afternoon";fi
test "$(whoami)" != 'root' && (echo you are using a/
non-privileged account;exit 1)
exit 0
#!/bin/sh
echo "every one:"
for i in $@
do echo $i
done
echo "total:"
echo $#
echo "the 1st:"
echo $1
echo "shift 1,show 1st"
shift 1
echo $1
exit 0
#!/bin/sh
foo=0
echo "Enter password"
read trythis
while [ $trythis != "secret" ];
do
echo "Sorry, try again"
foo=$(($foo+1))
if [ $foo -eq 3 ];then exit 1;fi
read trythis
done
exit 0
#!/bin/sh
# 一个自动登录ftp下载的shell程序
USER=anonymous
PASS=
ftp -i -n <<END
open xxx.xxx.xxx.xxx
user $USER $PASS
ls
get filename
close
END
cat /etc/shells 可查看已知的shell
/bin/dash 更符合标准,向前兼容
/bin/sh->bash 执行脚本快,但交互弱些
脚本的执行方法:
1.sh ./script.sh
2.chmod +x script.sh; ./script.sh
脚本开头表明解释器:
#!/bin/sh
#!/usr/bin/sed
#!/usr/bin/perl
#!/usr/bin/python
非交互式的shell就是脚本,交互式的就是应答式的。
脚本执行用的机制是fork-and-exec,内建命令执行不需要fork新的进程
shell的内建命令可以用man bash-buildins查看,用which命令查不到的是内建命令
$ cd ..; ls
$ (cd ..; ls) 这不会改变当前路径,因为带()会创建子进程来运行
关于bash启动文件:
一.交互式意味着可以输入命令:
登录shell就是系统验证完用户名和密码后得到的shell
启动读取/etc/profile ~/.bash_profile or ~/.bash_login or ~/.profile or /etc/bash.bashrc
非登录shell就是通过图标或菜单打开一个终端,如xterm,得到的shell
启动读取~/.bashrc
二.非交互式是为执行脚本而fork出来的shell
启动文件1.由变量BASH_ENV定义 2.以sh命令调用,读取的文件是/etc/profile或~/.profile
变量的类型:
环境变量->可以由当前shell传递给子进程使用,env或printenv可以查看,常用的有PATH,PWD,USER,SHELL等
本地变量->只存在于当前shell,使用内建命令set可显示所有变量(含环境变量)和函数
设置变量:
VARNAME="value" 本地变量
export VARNAME="value" 设为环境变量,子shell可以使用
shell中变量无需要特别声明,直接使用就可以了.??
删除变量: 使用内建命令unset VARNAME
读取变量:
$echo $VAR
$echo $VARabc
$echo ${VAR}abc {}用于这种场合
代换substitution
文件名代换globbing: * ? []
$ls /dev/sda*
$ls tw?.doc
$ls tw[0-9].doc 在shell中完成替换,再传到命令ls
反引号backquote命令代换:
DATE=`date` 表示内部是条命令,而不是一个字串或变量
$echo $(date) $()里面是命令,与`date`同
算术代换$((expression))
expression当中只能有 + - * / 和 () ,只能做整数运算
引用字符->用来去除字符或字的特殊含义
/ 可用来去除单个字符的特殊意义
' 可用来保持' '内所有字符的字面值
" 可用来保持" "内所有字符的字面值( 其实与不加引号情况同),下列情况除外:
- ` 反引号用于命令替换
- $VAR 显示变量内容 ?
- /$ /' /" // 分别用于表达$ ' " /这是个字符
- /用于其他情况,仅表示/
如:$ echo $'a/tb' 结果显示:$ a b
进程IO:
0表示标准输入,1表示标准输出,2表示标准错误输出
可以通过> < | 改变默认的输入输出方向
如:command > /dev/null 2>&1
/dev/null用于吸收数据并抛弃, 标准输出已经被改变了,因此&1已经指向了/dev/null,无任何信息输出
bash脚本编程:
参数与特殊变量:
$0 所执行脚本的路径名或函数名
$n n>0,表示第n个参数
$# 参数的个数
$* 所有参数一起括起来组成"$1 $2 ... $n"
$@ 所有参数单独括起来组成"$1" "$2" ... "$n"
$? 上一条命令的退出状态
$$ 当前shell的进程号
$! 上一条后台命令产生的进程的进程号
set用于把一个串作为当前脚本的参数,可用$n等进行提取,如
set $(date)
echo month is $2
unset命令用于删除变量,如
foo="hello"
unset foo
内建命令shift用于移动参数序列,如
shift 2 后原来的$3成为了$1
eval 可将显示的内容当命令执行,如
echo hello > out
eval echo hello > out
函数:函数在被使用后保留,除非在他们被使用后进行unset,set可显示,which可显示;
function FUNCTION { COMMANDS; }
或
FUNCTION () { COMMANDS; }
FUNCTION abc 引用时,函数名后面跟的就是传递给函数的参数
函数调用可以传递参数,函数内可以使用$n等系列字符引用,函数内可设置变量,用内建命令local可设置局部变量,函数返回用return value
shell的结束用exit value返回值给父进程,如exit 1表示错误,成功时返回0;
if条件判断表达式,正确返回0:
[ -d FILE ] 判断是否为目录
[ -f FILE ] 判断是否文件存在
[ FILE1 -nt FILE2 ] 1比2新或1在2不在,为真 ot 相反 ef 相同的设备和节点号,为真
[ STRING1 == STRING2 ] 字符串比较,!=, <, >
[ ARG1 OP ARG2 ] 数字比较,OP为 -eq 等于, -ne不等, -lt小于, -le小等, -gt大于, -ge大等
[ ! EXPR ]
[ ( EXPR ) ]
[ EXPR1 -a EXPR2 ] 表示and
[ EXPR1 -o EXPR2 ] 表示or
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
if TEST1; then
CONSE1
elseif TEST2; then
CONSE2
esle CONSE3;
fi
其中空格不能少, ; 可以用回车取代; 若then 或 else 后面没有要执行的语句,则用 then : 或 else :
case EXPRESSION in
CASE1) COMMAND-LIST;;
CASE2) COMMAND-LIST;;
...
CASEn) COMMAND-LIST;;
esac
for NAME in LIST; do COMMANDS; done
while CONTROL-COMMAND; do CONSEQUENT-COMMANDS; done
经典:
LOOP=1
while read LINE
do
echo "$LOOP: $LINE"
LOOP=`expr $LOOP + 1 `
done < $FILENAME 可以从文件读入行,并显示行号
mkdir $DIRECTORY > /dev/null 2>&1 可以创建目录,并不显示任何信息
test COND1 && COMMANDS short circuit 前面的条件成立,才执行后面
[ COND1 ] || COMMANDS test与[ ]作用相同,前面的条件不成立,才执行后面
基于这个逻辑false && * = false true || * = true
stop)
echo -n "shutting down"
kill `cat /var/run/sshd2_$PORT.pid` 直接获取cat出来的pid,让kill使用
echo "done."
;;
restart)
$0 stop 直接调用shell本身作为命令
$0 start
;;
一些实例:
#!/bin/sh
ls -la
pwd
exit 0
#!/bin/sh
echo "Is it morning? Please answer yes or n"
read timeofday
case "$timeofday" in
yes | y | Yes | YES)
echo "Good morning"
echo "Up bright and early this morning"
;;
[nN]*)
echo "Good afternoon"
;;
*)
echo "Sorry, answer not recognized"
echo "Please answer yes or no"
exit 1
;;
esac
exit 0
#!/bin/sh
OUTPUT="> out.file"
echo hello $OUTPUT
OUTPUT="> out.file"
eval echo hello $OUTPUT
#!/bin/sh
for foo in bar fud 43
do
echo $foo
done
for i in 1 2 3
do
echo $(($i+$i))
done
exit 0
#!/bin/sh
yes_or_no()
{
echo "Is your name $* ?" $*是传进函数的参数
while true 确保不输入或输入错误还有机会输入
do
echo -n "Enter yes or no: " -n表示不换行
read x
case "$x" in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo "Answer yes or no" 给输入错误用while提供足够机会
esac
done
}
#the main part of the program begins:
echo " Original parameters are $*"
if yes_or_no "$1" 把$1作为参数传给函数yes_or_no
then
echo "Hi $1, nice name"
else
echo "Never mind"
fi
exit 0
#!/bin/sh
if test -f /bin/bash;then echo "file /bin/bash exists";fi ;分割符号也可用回车代替
if [ -d /bin/bash ];then echo "/bin/bash is a directory"
else echo "/bin/bash is NOT a directory";fi
#!/bin/sh
echo "Is it morning? Please answer yes or no"
read timeofday
if [ $timeofday = "yes" ];then echo "Good morning"
else echo "Good afternoon";fi
test "$(whoami)" != 'root' && (echo you are using a/
non-privileged account;exit 1)
exit 0
#!/bin/sh
echo "every one:"
for i in $@
do echo $i
done
echo "total:"
echo $#
echo "the 1st:"
echo $1
echo "shift 1,show 1st"
shift 1
echo $1
exit 0
#!/bin/sh
foo=0
echo "Enter password"
read trythis
while [ $trythis != "secret" ];
do
echo "Sorry, try again"
foo=$(($foo+1))
if [ $foo -eq 3 ];then exit 1;fi
read trythis
done
exit 0
#!/bin/sh
# 一个自动登录ftp下载的shell程序
USER=anonymous
PASS=
ftp -i -n <<END
open xxx.xxx.xxx.xxx
user $USER $PASS
ls
get filename
close
END