从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁(本质上来说,Shell每一行可以看做一个命令)。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修 功课之一。
Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh),等等。不同的Shell语言的语法有所不同,所以不能交换使用。每种Shell都有其特色之处,基本上,掌握其中任何一种 就足够了。在本文中,我们关注的重点是Bash,也就是Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用;同时,Bash也是大多数Linux系统默认的Shell。在一般情况下,人们并不区分 Bourne Shell和Bourne Again Shell,所以,在下面的文字中,我们可以看到#!/bin/sh,它同样也可以改为#!/bin/bash。
利用vi等文本编辑器编写Shell脚本的格式是固定的,如下:
#!/bin/sh
首行中的符号#!告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程序。如果首行没有这句话,在执行脚本文件的时候,将会出现错误。
#comments
后续的部分就是主程序,Shell脚本像高级语言一样,也有变量赋值,也有控制语句。除第 一行外,以#开头的行就是注释行,直到此行的结束。如果一行未完成,可以在行尾加上",这个符号表明下一行与此行会合并为同一行。
Your commands go here
一个小的示例程序
#!/bin/sh
#example of shell
echo "first example"
echo #this inserts an empty line in output
echo "we are currently in the following directory"
/bin/pwd
echo
echo "this directory contains the following files"
/bin/ls
用户需要执行一个脚本,需要对这个脚本具有r权限,对脚本所在目录有rx权限。
l 创建一个包含命令和控制结构的文件。
l 修改这个文件的权限使它可以执行,使用chmod u+x
l 执行./example或者sh example
变量时shell传递数据的一种方法,用来代表每个取值的符号名。
Shell Script有两种类型的变量:
l 临时变量:Shell程序内部定义的,其使用范围仅限于定义它的程序,对其他程序不可见。包括了用户自定义变量、位置变量。
l 永久变量:永久变量是环境变量,其值不随shell脚本的执行结束而消失。
Shell Script是一种弱类型语言,其使用变量的时候不需要首先声明其类型。新的变量会在本地数据区分配内存进行存储,这个变量归当前的Shell所有,任何子进程都不能访问本地变量。
变量的辅助命令:
set |
查看系统中所有定义的变量 |
unset 变量名 注意,不要加$符号 |
删除变量 |
变量赋值的方式是:
variable_name=variable_value
【注意】赋值号两边应该没有空格,否则variable_name会被当做一个命令。
如果对一个已经有值的变量赋值,新值将取代旧值。取值的时候要在变量名前加$,$variable_name可以在引号中使用,这一点和其他高级语言是明显不同的。如果出现混淆的情况,可以
v 用花括号来区分,例如:
echo "Hi, $as"
就不会输出“Hi, hello worlds”,而是输出“Hi,”。这是因为Shell把$as当成一个变量,而$as未被赋值,其值为空。正确的方法是:
echo "Hi, ${a}s"
v 使用单引号,单引号中的变量不会进行变量替换操作。
譬如:$ABC = “time is $Date”,echo $ABC的值为:time is 2013-12-4
$ABC = ‘time is $Date’,echo $ABC的值为:time is $Date
Shell 解释执行用户命令时,将命令行的第一个部分(实际的命令加选项)作为命令名,其它部分作为参数。由出现在命令行上的位置确定的参数称为位置参数。例如:
ls -l file1 file2 file3
$0 这个程序的文件名 ls -l
$n 这个程序的第n个参数值,n=1-9
特殊变量
$* |
这个程序的所有参数 |
$# |
这个程序的参数个数 |
$$ |
这个程序的PID |
$! |
执行上一个后台命令的PID |
$? |
执行上一个命令的返回值。 返回值为0表示上一个命令执行成功。 返回值不为0表示错误代码。 |
基本类型 |
定义时赋值,如NUM=1,$NAME=’Zhang’或者$NAME=”张”。 |
命令类型 |
将一个命令的执行结果赋给变量,如:TIME=`date`,此时`称为命令替换符或者TIME=$(date)。 |
变量 |
将一个变量赋值给另一个变量,如:A=$B |
变量测试语句用于测试变量是否相等,是否为空,以及文件类型等等,基本格式为:
test 测试条件
-b filename |
当filename 存在并且是块文件时返回真(返回0) |
-c filename |
当filename 存在并且是字符文件时返回真 |
-d pathname |
当pathname 存在并且是一个目录时返回真 |
-e pathname |
当由pathname 指定的文件或目录存在时返回真 |
-f filename |
当filename 存在并且是正规文件时返回真 |
-g pathname |
当由pathname 指定的文件或目录存在并且设置了SGID 位时返回真 |
-h filename |
当filename 存在并且是符号链接文件时返回真 (或 -L filename) |
-k pathname |
当由pathname 指定的文件或目录存在并且设置了"粘滞"位时返回真 |
-p filename |
当filename 存在并且是命名管道时返回真 |
-r pathname |
当由pathname 指定的文件或目录存在并且可读时返回真 |
-s filename |
当filename 存在并且文件大小大于0 时返回真 |
-S filename |
当filename 存在并且是socket 时返回真 |
-t fd |
当fd 是与终端设备相关联的文件描述符时返回真 |
-u pathname |
当由pathname 指定的文件或目录存在并且设置了SUID 位时返回真 |
-w pathname |
当由pathname 指定的文件或目录存在并且可写时返回真 |
-x pathname |
当由pathname 指定的文件或目录存在并且可执行时返回真 |
-O pathname |
当由pathname 存在并且被当前进程的有效用户id 的用户拥有时返回真(字母O 大写) |
-G pathname |
当由pathname 存在并且属于当前进程的有效用户id 的用户的用户组时返回真 |
file1 -nt file2 |
file1 比file2 新时返回真 |
file1 -ot file2 |
file1 比file2 旧时返回真 |
f1 -ef f2 |
files f1 and f2 are hard links to the same file |
-a |
逻辑与,操作符两边均为真,结果为真,否则为假。 |
-o |
逻辑或,操作符两边一边为真,结果为真,否则为假。 |
! |
逻辑否,条件为假,结果为真。 |
举例: [ -w result.txt -a -w score.txt ] ;echo $? // 测试两个文件是否均可写
-z string |
字符串string 为空串(长度为0)时返回真 |
-n string |
字符串string 为非空串时返回真 |
str1 = str2 |
字符串str1 和字符串str2 相等时返回真 |
str1 == str2 |
同 = |
str1 != str2 |
字符串str1 和字符串str2 不相等时返回真 |
str1 < str2 |
按字典顺序排序,字符串str1 在字符串str2 之前 |
str1 > str2 |
按字典顺序排序,字符串str1 在字符串str2 之后 |
举例: name="zqf"; [ $name = "zqf" ];echo $? // 打印 0 表示变量name 的值和字符串"zqf"相等
常见数值测试
int1 -eq int2 |
如果int1 等于int2,则返回真 |
int1 -ne int2 |
如果int1 不等于int2,则返回真 |
int1 -lt int2 |
如果int1 小于int2,则返回真 |
int1 -le int2 |
如果int1 小于等于int2,则返回真 |
int1 -gt int2 |
如果int1 大于int2,则返回真 |
int1 -ge int2 |
如果int1 大于等于int2,则返回真 |
在 (()) 中的测试:
< |
小于(在双括号里使用) |
(("$a" < "$b")) |
<= |
小于等于 (在双括号里使用) |
(("$a" <= "$b")) |
> |
大于 (在双括号里使用) |
(("$a" > "$b")) |
>= |
大于等于(在双括号里使用) |
(("$a" >= "$b")) |
变量测试语句一般不单独使用,一般作为if语句的测试条件,譬如:
if test -d $1 then
...
elif 条件2 then
...
fi
而变量测试语句可用[]进行简化,譬如:
test -d $1
等价于
[ -d $1 ]
case 变量 in
字符串1) 命令列表1
;;
...
*) 通用命令
;;
esac
while 条件
do
命令
done
until 条件
do
命令
done
until类似于while循环,不同的是until是条件返回值为假时才继续执行。
for 变量 in 名字列表
do
命令列表
done
#!/bin/sh
for DAY in Sunday Monday Tuesday
do
echo "The day is : $DAY"
done
For语句也可以如下使用:
Vi hello.sh
#!/bin/bash
for((i=0;i<3;i++))
do
echo $i
done
v continue:继续当前循环
v break:跳出当前循环
read |
从键盘读入数据,赋值给变量 使用格式: read UserName Para1 Para2,... |
注意 l 输入参数个数小于定义,则缺省不赋值。 l 输入参数个数多于定义,则多余的参数会一次性赋给最后一个变量。 |
select |
select 变量 in 关键字 do command 1 ... command n done
|
#!/bin/sh echo "You choice?" select var in "a" "b" "c";do break; done echo $var 如果提示select not found,则 把#!/bin/sh改为#!/bin/bash 执行脚本时将sh select.sh改为 bash select.sh |
tee |
读取标准输入的数据,并将其内容输出成文件。 语法: tee [-ai][--help][--version][文件...] 参数: -a或--append :附加到既有文件的后面,而非覆盖它. -i-i或--ignore-interrupts :忽略中断信号。 --help :在线帮助。 --version :显示版本信息。 |
|
expr |
对整数型变量进行算术运算,注意运算参数之间的空格不可以省略。 |
expr 3 + 5 expr $var1 - 5 expr $var1 / $var2 expr $var3 \* 10 乘法需要转义字符 |
let |
let 是 bash 內建的整數運算。arg 代表運算式。 當得到的值為 0,回應狀態值 1,其他數值,則回應狀態值 0。 |
#!/bin/bash let "t1 = ((a = 5 + 3, b = 7 - 1, c = 15 - 4))" echo "t1 = $t1, a = $a, b = $b" #!/bin/bash let a=5+4 b=9-3 echo $a $b |
exit |
退出程序执行,并返回一个返回码,返回码为0表示正常退出,非0表示非正常退出 |
exit 0 |
wc 命令
Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数、字数、行数,并将统计结果显示输出。 |
1.命令格式: wc [选项]文件... 2.命令功能: 统计指定文件中的字节数、字数、行数,并将统计结果显示输出。该命令统计指定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所指定文件的总统计数。 3.命令参数: -c 统计字节数。 -l 统计行数。 -m 统计字符数。这个标志不能与 -c 标志一起使用。 -w 统计字数。一个字被定义为由空白、跳格或换行字符分隔的字符串。 -L 打印最长行的长度。 -help 显示帮助信息 --version 显示版本信息 |
实例1:查看文件的字节数、字数、行数 命令: wc test.txt |
awk -F |
域分隔符‘命令’ |
示例: 1、检测系统中UID为0的用户 awk -F: ‘$3==0 {print $1}’ /etc/passwd 2、检测系统中密码为空的用户 awk -F: ‘length($2)==0 {print $1}’ /etc/shadow |
sh -x script:使用+号表示脚本执行情况。
sh -n script:不执行脚本只是进行脚本的语法检测,返回所有的错误。
1创建shell:
touch /opt/soft/bin/auto-del-30-days-ago-log.sh
chmod +x auto-del-30-days-ago-log.sh
新建一个可执行文件auto-del-30-days-ago-log.sh,并分配可运行权限
2编辑shell脚本:
vi auto-del-30-days-ago-log.sh
编辑auto-del-30-days-ago-log.sh文件如下:
#!/bin/sh
find /opt/soft/log/ -mtime +30 -name "*.log" -exec rm -rf {} \;
3计划任务:
#crontab -e
将auto-del-30-days-ago-log.sh执行脚本加入到系统计划任务,到点自动执行
输入:
10 0 * * * /opt/soft/log/auto-del-7-days-ago-log.sh >/dev/null 2>&1
这里的设置是每天凌晨0点10分执行auto-del-7-days-ago-log.sh文件进行数据清理任务了。
查找当前目录中所有大于500M的文件,把这些文件名写到一个文本文件中,并统计其个数。 |
find ./ -size +500M -type f | tee file_list | wc -l |
在目录/tmp下找到100个以abc开头的文件,然后把这些文件的第一行保存到文件new中。 |
for filename in `find /tmp -type f -name "abc*"|head -n 100` do sed -n '1p' $filename>>new done |
每隔10分钟监控一次,监控/usr下如果大于5G,发邮件给管理员 |
#!/bin/bash while true do sleep 600 n=$(du -s /usr | cut -f1) if [ $n -gt 5242880 ] then mail -s "greater" filwsyl@gmail.com < ~/filename #将文件filename的内容发送出去。 fi done |
监控指定路径的文件