这里有我在51cto的博客,写了一部分shell编程最基本的东西:
http://blog.51cto.com/13132323/2043713
shell脚本语言的种类
- bourne shell(包括sh,ksh和bash)
- C shell
- 他是一个弱脚本语言,对语法要求不是很严格,本博客主要针对bash,因为是linux,默认bash
查看默认Linux的默认shell
- 也就是直接取变量直接打印 echo $SHELL
linux 中shell的组成
- 脚本开头:#!/bin/bash
指出由那个解释器执行我们的脚本,#!又称为幻数,在执行bash脚本的时候,内核会根据它来确定用哪个程序来解释脚本中的任务,这一行必须在第一行,否则被视为注释。 - 脚本动作
- 脚本注释
执行脚本的基本区别
source或者’.’执行可以将脚本的的变量的值或者函数传入当前副脚本的shell中,而不像+x 权限和/bin/sh是创建一个子shell来执行脚本
shell的变量类型
- 环境变量(全局变量)
当前环境shell和任意子shell中都可以使用,就先当于在/etc/profile.d/下的目录放了相应的脚本,登录默认执行。 - 局部变量
只能在当前环境中使用
这里说连个优化: TMOUT和HISTSIZ分别是待机时间,历史记录的数量
自定义环境变量(unset 是取消变量)
- 使用export(-f,-n,-p参数)
export 变量名=value
变量名=value,export 变量名 - 使用declare
declare -x 变量名=value
单引号和双引号区别
单引号
强引用,所见即所得,单引号的内容在输出的时候,会按照原样输出
双引号
弱引用,在输出前,会将变量解析之后,在输出
[loveyu@feitian ~]$ a=172.25.254.7
[loveyu@feitian ~]$ echo $a
172.25.254.7
[loveyu@feitian ~]$ a=172.25.254.7-$a
[loveyu@feitian ~]$ echo $a
172.25.254.7-172.25.254.7
[loveyu@feitian ~]$ b='172.25.254.7-$a'
[loveyu@feitian ~]$ echo $b
172.25.254.7-$a
[loveyu@feitian ~]$ c="172.25.254.7-$a"
[loveyu@feitian ~]$ echo $c
172.25.254.7-172.25.254.7-172.25.254.7
把一个命令作为变量
- 方式一
[loveyu@feitian ~]$ CMD=`ls`
[loveyu@feitian ~]$ echo $CMD
fenghui tools
- 方式二
[loveyu@feitian ~]$ echo $(date +%F)
2017-11-21
sehll中特殊变量
位置变量
- $0 获取当前执行的shell脚本的文件名,包括路径,也就是脚本的名称
注意:在执行脚本前面加上dirname或者basename,就是只取出文件名称称和路径名称 - $n 获取当前执行的shell脚本的第n个参数值,如果大于9,用大括号括起来{10}
- $@ 这个程序的所有参数 “$1”,”$2”,”……”,这个是将所有参数传递给其他程序的最佳方式,因为他会保留所有内嵌在每个参数里的任何空白。
- $# 获取当前shell命令中参数的总个数
- $* 获取当前shell的所有参数,将所有的命令行参数视为单个字符串,相当于”$1$2”,注意和$#的区别
注意:在/etc/init.d/network 的最后有这样一行,也就是我们的$0
echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}"
[loveyu@feitian /]$ /etc/init.d/network dfsf
Usage: /etc/init.d/network {start|stop|status|restart|reload|force-reload}
[loveyu@feitian /]$ dirname /etc/init.d/network
/etc/init.d
[loveyu@feitian /]$ basename /etc/init.d/network
network
$*和$@的区别
$* 是将所有的参数变成一个字符串,而$@是将每个参数视为一个字符串
[loveyu@feitian /]$ set -- "I am " handsome feitian
[loveyu@feitian /]$ for i in $*;do echo $i;done
I
am
handsome
feitian
[loveyu@feitian /]$ for i in $@;do echo $i;done
I
am
handsome
feitian
[loveyu@feitian /]$ for i in "$*";do echo $i;done
I am handsome feitian
[loveyu@feitian /]$ for i in "$@";do echo $i;done
I am
handsome
feitian
#可以注意到,他俩不加引号,没有区别看,如果就是上面所说的作用
进程状态变量
- $$ 获取当前shell的进程号
- $!获取上一个命令的PID,上一个后台运行进程的进程号
- $? 获取上一个命令的返回值(0为成功,非零为失败),返回值如下:
0 表示运行成功
2 表示权限被拒
126 表示找到该命令了,但是无法执行
127 表示未找到要运行的命令
128 表示命令被系统强制结束
- $_在此之前执行的命令(上一个命令)或者脚本的最后一个参数
利用getopts处理命令行选项
[root@feitian day1]# cat getopt.sh
#!/bin/sh
while getopts lrt options
do
case $options in
l) echo "you entered $options" ;;
r) echo "you entered $options" ;;
t) echo "you enetered $options" ;;
esac
done
[root@feitian day1]# sh getopt.sh -lrt
you entered l
you entered r
you enetered t
#他就是将lrt赋值给options,然后用swtch case语句,如果有l,就将l打印出来,如果有r,就将r打印出来。还有要注意,如果在执行脚本时候不加-他就会接收不到,而且他只能接收lrt,也就是你脚本中定义的。
练习
- 只允许管理员使用脚本清空日志
#!/bin/bash
if [ "$UID" -ne "0" ]
# -ne是不等于的意思
then
echo "must be root run this scripts"
exit
fi
cd $LOG_DIR|| {
echo "cannot change to neccessary directory" >&2
exit 1
}
#这里我们有两层筛选,如果他的UID不等于0或者进不了root的家目录,则直接退出
cat /dev/null >/var/log/messages && echo "Logs cleaned up"
- 理解下面这个脚本语句
runlevel=$(set – $(runlevel); eval “echo $$#” )
(set -- $(runlevel);
他的意思是将runlevel命令所执行的结果当成参数个数
eval "echo \$$#"
他的意思是执行后面的echo,将他当成命令执行,而$$#,则就等于runlevel命令执行结果的个数,也就是$2。