为什么学习shell编程?
对于一个合格的系统管理员来说,学习和掌握Shell编程是非常重要的。通过编程,可以在很大程度上简化日常的维护工作,使得管理员从简单的重复劳动中解脱出来。
Shell程序的特点:
1、简单易学。
2、解释性语言,不需要编译即可执行。
shell 特性
什么是shell???
Shell又称命令解释器,它能识别用户输入的各种命令,并传递给操作系统。它的作用类似于Windows操作系统中的命令行,但是,Shell的功能远比命令行强大的多。在UNIX或者localhost中,Shell既是用户交互的界面,也是控制系统的脚本语言。
shell种类
Bourne Shell:标识为sh,该Shell由Steve Bourne在贝尔实验室时编写。在许多Unix系统中,该Shell是root用户的默认的Shell。
Bourne-Again Shell:标识为bash,该Shell由Brian Fox在1987年编写,是绝大多数linux发行版的默认的Shell。
Korn Shell:标识为ksh,该Shell由贝尔实验室的David Korn在二十世纪八十年代早期编写。它完全向上兼容 Bourne Shell 并包含了C Shell 的很多特性。
C Shell:标识为csh,该Shell由Bill Joy在BSD系统上开发。由 于其语法类似于C语言,因此称为C Shell。
什么是shell脚本?
当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行,该程序文件即脚本。
Shell脚本运行
方法一:切换到shell脚本所在的目录(此时,称为工作目录)执行shell脚本
方法二:以绝对路径的方式去执行bash shell脚本:
方法三:直接使用bash 或sh 来执行bash shell脚本:
方法四:在当前的shell环境中执行bash shell脚本:source或.
区别: 1、方法三:可以在脚本中不指定解释器,脚本可以没有执行权限
2、方法一和方法二脚本需要有执行权限,./script_name.sh 或 /path/script_name.sh
3、方法四:当前shell执行,方法1-3开启子shell
shell脚本注释
通过在代码中增加注释可以提高程序的可读性。传统的Shell只支 持单行注释,其表示方法是一个井号“#”,从该符号开始一直到行尾都属于注释的内容。
多行如何注释?
用户还可以通过其他的一些变通的方法来实现多行注释,其中,最简单的方法就是使用冒号“:”配合here document,语法如下:
:<<BLOCK
注释内容
BLOCK
shell脚本规范
开头指定脚本解释器
#!/bin/sh或#!/bin/bash
其他行#表示注释
名称见名知义 backup_mysql.sh,以sh结尾
#Date: 创建日期
#Author: 作者
#Mail: 联系方式
#Function: 功能
#Version: 版本
Shell中会使用的一些查询命令
Vi及Vim解析
Grep及基本正则表达式
正则表达式就是为了处理大量的文本|字符串而定义的一套规则和方法
对于系统管理员来讲,正则表达式贯穿在我们的日常运维工作中,无论是查找某个文档,抑或查询某个日志文件分析其内容,都会用到正则表达式
Linux中的正则表达式,常应用正则表达式的命令是grep(egrep),sed,awk。Linux下三剑客!
正则表达式分为两种:
- 基本正则表达式(BRE,basic regular expression)
- 扩展正则表达式(ERE,extended regular expression)
参数 | 作用 |
---|---|
–color | 匹配到的字符串显示颜色 |
-i | 忽略字符大小写 |
-o | 仅显示匹配的字串 |
-v | 反向选取, 即显示不匹配的行 |
-E | 使用扩展正则表达式 |
-n | 显示行号 |
-w | 匹配指定的字符串 |
元数据 | 意义和范例 |
---|---|
^word | 搜寻word开头的行 |
搜寻以#开头的行,grep -n ‘^#’ file | |
word$ | 搜寻word结尾的行 |
搜寻以.结尾的行,grep -n ‘.$’ file | |
. | 匹配任意一个字符 |
匹配e和e之间有任意一个字符,grep -n ‘e.e’ file | |
\ | 转义字符 |
* | 前面的一个字符重复0到多次 |
匹配gle,gogle,google,gooogle等,grep -n ‘go*gle’ file | |
[list] | 匹配一系列字符中的一个 |
[n1-n2] | 匹配一个字符范围中的一个字符 |
匹配数字字符,grep -n ‘[0-9]’ file | |
[^list] | 匹配字符集以外的字符 |
匹配非o字符,grep -n ‘[^o]’ file | |
{n1,n2} | 前面的单个字符重复n1,n2次 |
匹配google,gooogle,grep -n ’ go{2,3}gle ’ file | |
<word | 单词的开头 |
匹配以g开头的单词,grep -n <g file | |
word> | 单词的结尾 |
匹配以tion结尾的单词,grep -n tion> file | |
‘ ‘ | 强引用,引号内的内容不变 |
“ ” | 弱引用,变量会替换 |
[[:alnum:]] | 代表英文大小写字符及数字,即 0-9, A-Z, a-z |
[[:alpha:]] | 代表任何英文大小写字符,即 A-Z, a-z |
[[:space:]] | 任何会产生空白的字符,包括空白键, [Tab] 等等 |
[[:digit:]] | 代表数字,即 0-9 |
[[:lower:]] | 代表小写字符,即 a-z |
[[:upper:]] | 代表大写字符,即 A-Z |
egrep及扩展正则表达式
grep一般情况下支持基本正则表达式,可以通过参数-E支持扩展正则表达式,另外grep单独提供了一个扩展命令叫做egrep用来支持扩展正则表达式,这条命令和grep -E等价(grep -E == egrep)
一般情况下,基本正则表达式就够用了
特殊情况下,复杂的扩展表达式,可以简化字符串的匹配
扩展正则表达式就是在基本正则表达式的基础上,增加了一些元数据
元数据 | 意义和范例 |
---|---|
+ | 重复前面字符1到多次 |
匹配god,good,goood等字符串,grep -nE go+d’ file | |
? | 匹配0或1次前面的字符 |
匹配gd,god,grep -nE ‘go?d’ file | |
| | 或or的方式匹配多个字符串 |
匹配god或者good,grep -nE’god | |
() | 匹配整个括号内的字符串,原来都是匹配单个字符 |
搜索good或者glad,grep -nE 'g(oo | |
* | 前面的字符重复0到多次 |
例题:
#1、显示/proc/meminfo文件中以大小s开头的行(要求:使用两种方法)
cat /proc/meminfo |grep -i "^s" cat /proc/meminfo |grep "^\(s\|S\)"
#2、显示/etc/passwd文件中不以/bin/bash结尾的行
cat /etc/passwd |grep -v ":/bin/bash$"
#3、显示用户rpc默认的shell程序
cat /etc/passwd |grep -w "^rpc" |cut -d: -f 7
#4、找出/etc/passwd中的两位或三位数
cat /etc/passwd |grep -wo "[[:digit:]]\{2,3\}"
#5、显示CentOS7的/etc/grub2.cfg文件中,至少以一个空白字符开头的且后面有非空白字符的行
cat /etc/grub2.cfg | grep "^[[:space:]]\+[^' '].*" cat /etc/grub2.cfg | grep "^[[:space:]]\+[^[:space:]].*"
#6、找出“netstat -tan”命令结果中以LISTEN后跟任意多个空白字符结尾的行
netstat -tan | grep ".*LISTEN[[:space:]]*$"
#7、显示CentOS7上所有系统用户的用户名和UID
cat /etc/passwd |cut -d: -f1,3 |grep -w "[1-9][0-9]\{,2\}$"
#8、添加用户bash、testbash、basher、sh、nologin(其shell为/sbin/nologin),找出/etc/passwd用户名和shell同名的行
cat /etc/passwd |grep -w "^\([^:]*\):.*/\1$"
#9、利用df和grep,取出磁盘各分区利用率,并从大到小排序
df |grep "^/dev/sd"|grep -wo "[0-9]\+%"|sort -nr
#10、显示三个用户root、mage、wang的UID和默认shell
cat /etc/passwd |grep -w "^\(root\|mage\|wang\)" |cut -d: -f 3,7
#11、找出/etc/rc.d/init.d/functions文件中行首为某单词(包括下划线)后面跟一个小括号的行
cat /etc/rc.d/init.d/functions |grep -i "^\([_[:alnum:]]\+(\)"
#12、使用egrep取出/etc/rc.d/init.d/functions中其基名
echo "/etc/rc.d/init.d/functions" | grep -Eo "[^/]*[/]?$"|tr -d "/"
#13、使用egrep取出上面路径的目录名
echo "/etc/rc.d/init.d/" | grep -Eo ".*[/]\<"
#14、统计last命令中以root登录的每个主机IP地址登录次数
last |grep -w "^root.*\<pts" | grep -wE "((([0-9])|([1-9][0-9])|(1[0-9]{2})|(2[0-4][0-9])| (25[0-5]))[.]){3}(([0-9])|([1-9][0-9])|(1[0-9]{,2})|(2[0-4][0-9])|(25[0-5])){1}[[:space:]]" |tr -s " "|cut -d " " -f3|sort|uniq -c
#15、利用扩展正则表达式分别表示0-9、10-99、100-199、200-249、250-255
[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]
#16、显示ifconfig命令结果中所有IPv4地址
ifconfig |grep -owE "((([0-9]{1,2})|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))[.]){3}(([0-9] {1,2})|(1[0-9]{,2})|(2[0-4][0-9])|(25[0-5])){1}[[:space:]]"
#17、将此字符串:welcome to magedu linux 中的每个字符去重并排序,重复次数多的排到前面
cat test | grep -o "[[:lower:]]"|sort |uniq -c|sort -nr |tr -s ' ' | cut -d " " -f 3 |tr -d '\n'
#18.找出ifconfig命令结果中的1-255之间的数字
ifconfig | grep -E "\<([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>"
#步骤拆解 #2
#25
#255
要注意规律
#ifconfig | grep -E "\<([1-9]|[1-9][0-9])\>" #| 为或
#ifconfig | grep -E '\<(1[0-9][0-9])\>'
#ifconfig | grep -E '\<(2[0-4][0-9]|25[0-5])\>'
截取文件test内的内容:
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
Football game is not use feet only.
this dress doesn’t fit me.
However, this dress is about $ 3183 dollars.^M
GNU is free air not free beer.^M
Her hair is very beauty.^M
I can’t finish the test.^M
Oh! The soup taste good.^M
motorcycle is cheap than car.
This window is clear.
the symbol ‘*’ is represented as start.
Oh! My god!
The gd software is a library for drafting programs.^M
You are the best is mean you are the no. 1.
The world is the same with “glad”.
I like dog.
google is the best tools for search keyword.
goooooogle yes!
go! go! Let’s go.
# I am ghost
#19. 取得 test 或 taste 这两个单字相关信息及行号
grep -n 't[ae]st' test
#20.不想取 oo 前面有 g 的字符相关信息及行号
grep -n 'oo' test | grep -v 'goo'
#21.oo 前面不想要有小写的字符相关信息及行号
grep -n 'oo' test | grep -v '[[:lower:]]oo'
#22.取得行尾结束为小数点相关信息及行号
grep -n '\.$' test
#23.取得空白行相关信息及行号
grep -n '^$' test
#24.取得g??d 的字符相关信息及行号
grep -n 'g..d' test
#25.取得至少两个 o 以上的字符相关信息及行号
grep -n 'o\{2\}' test
#26.取得开头与结尾都是 g ,两个g 之间仅能存在至少一个 o 相关信息及行号
grep -n 'go*g' test
#27.取得g 开头与 g 结尾的字符,当中的字符可有可无 相关信息及行号
grep -n 'g.*g' test
#28.取得g 后面连接2到5个 o,然后再接一个 g 的字符相关信息及行号
grep -n 'go\{2,5\}g' test
sort 排序
sort 就是将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。
sort 参数 文件(-o 输出文件)
参数 | 作用 |
---|---|
-b | 忽略每行开头空格字符 |
-c | 检查文件是否已按顺序排序 |
-f | 忽略大小写 |
-M | 将前三个字母按月份进行排序 |
-n | 依照数值大小进行排序 |
-o | 输出文件 |
-r | 以从大到小排序 |
-t <分隔符> | 指定排序区间分隔符 |
-k | 选择排序区间 |
-u | 去除重复项 |
sort
[root@linux project]# cat test1
apple
xiaomi
google
apple
baidu
sohu
android
xunlei
[root@linux project]# sort test1
android
apple
baidu
google
sohu
xiaomi
xunlei
sort -u 去除重复行
[root@linux project]# cat test1
apple
xiaomi
google
apple
baidu
sohu
android
xunlei
[root@linux project]# sort -u test1
android
apple
baidu
google
sohu
xiaomi
xunlei
当-u和-k一起使用时,sort只会对-k参数指定的部分进行比较,若-k指定部分相同则一样会过滤掉不输出。
sort -r 将文件降序排列
[root@linux project]# cat test1
apple
xiaomi
google
apple
baidu
sohu
android
xunlei
[root@linux project]# sort -r test1
xunlei
xiaomi
sohu
google
baidu
apple
android
sort -o 将文件内容排序并输出到另一个文件
由于sort默认是把结果输出到标准输出,所以需要用重定向才能将结果写入文件,形如sort filename > newfile。
但如果想把排序结果输出到原文件中,用重定向可就不行了。
[rocrocket@rocrocket programming]$ sort -r number > number
[rocrocket@rocrocket programming]$ cat number
[rocrocket@rocrocket programming]$
number清空了!!!
所以在这里我们就需要用-o参数
[root@linux project]# cat number
1
3
5
2
4
[root@linux project]# sort -r number -o number
[root@linux project]# cat number
5
4
3
2
1
sort -n 以数值number大小排序
[root@linux project]# cat number
3
25
1
564
43
47
234
4
56725
1
324
4
32
[root@linux project]# sort number
1
1
234
25
3
32
324
4
4
43
47
564
56725
[root@linux project]# sort -n number
1
1
3
4
4
25
32
43
47
234
324
564
56725
sort -t 以指定的分隔符排序 -k 以指定的列进行排序
在sort排序中,-t如果不指定分隔符时,一般默认以空格作为分隔符
sort中-t和-k一般结合使用,例如:
[root@linux project]# cat test3
apple:50:5000
android:60:6000
google:100:5500
baidu:110:5500
sohu:55:7000
xiaomi:65:4000
xunlei:50:6000
[root@linux project]# sort -n -t: -k2 test3
apple:50:5000
xunlei:50:6000
sohu:55:7000
android:60:6000
xiaomi:65:4000
google:100:5500
baidu:110:5500
例题:
[root@linux project]# cat test2
apple 50 5000
android 60 6000
google 100 5500
baidu 110 5500
sohu 55 7000
xiaomi 65 4000
xunlei 50 6000
以空格作为分隔符,以第二列数据作为排序标准,若第二列中有重复数据则比较第三列:
[root@linux project]# sort -n -t' ' -k 2,3 test2
apple 50 5000
xunlei 50 6000
sohu 55 7000
android 60 6000
xiaomi 65 4000
google 100 5500
baidu 110 5500
以空格作为分隔符,只以第一列的第二位作为排序标准,若有重复数据则只比较第三列:
[root@linux project]# sort -n -t' ' -k 1.2,1.2 -k 3,3 test2
xiaomi 65 4000
apple 50 5000
baidu 110 5500
google 100 5500
android 60 6000
xunlei 50 6000
sohu 55 7000
注意这个例子
[root@linux project]# sort -n -k 2.2,3.1 test2
apple 50 5000
android 100 6000
google 100 5500
baidu 110 5500
以第二个域的第二个字符开始到第三个域的第一个字符结束的部分进行排序。
第一行,会提取0 5,第二行提取00 6,第三行提取00 5,第四行提取10 5。
又因为sort认为0小于00小于000小于0000….
原因:sort只会比较第二个域的第二个字符到第二个域的最后一个字符的部分,而不会把第三个域的开头字符纳入比较范围。当发现00和00相同时,sort就会自动比较第一个域去了。当然baidu在sohu前面了。
用一个范例即可证实:
[root@linux project]# sort -n -k 2.2,3.1 -k3.1 test2
apple 50 5000
google 100 5500
android 100 6000
baidu 110 5500
diff 命令
diff 命令用于比较多个文本文件的差异,格式为“diff [选项] 文件”。
使用 cat 命令分别查看 diff_A.txt 和 diff_B.txt 文件的内容,然后进行比较:
[root@linux ~]# cat diff_A.txt Welcome to linux.com
Red Hat certified Free Linux Lessons
[root@linux ~]# cat diff_B.txt
Welcome to linux.com #Red Hat certified
Free Linux Lessons
使用 diff --brief 命令显示比较后的结果,判断文件是否相同:
[root@linux ~]# diff --brief diff_A.txt diff_B.txt
Files diff_A.txt and diff_B.txt differ
使用带有-c 参数的 diff 命令来描述文件内容具体的不同:
[root@linux ~]# diff -c diff_A.txt diff_B.txt
*** diff_A.txt 2018-08-16 05:54:38.440500149 -0400
--- diff_B.txt 2018-08-16 05:55:09.921497634 -0400
***************
*** 1,3 ****
Welcome to linux.com
! Red Hat certified Free Linux Lessons
--- 1,3 ----
Welcome to linux.com
! #Red Hat certified
Free Linux Lessons
使用 diff 命令结合后面要讲的重定向,生成一个补丁文件,其中,字母"a"、“d”、"c"分别表示添加、删除及修改操作。
[root@linux mnt]# diff diff_A.txt diff_B.txt 1,3c1,3
< Welcome to linux.com
< #Red Hat certified
< Free Linux Lessons
---
> Welcome tooo linux.com
> Red Hat certified
> Free Linux LeSSonS
[root@linux mnt]# diff diff_A.txt diff_B.txt > path.file [root@linux mnt]# cat path.file
2c2
< Red Hat certified
---
> #Red Hat certified
参数 | 作用 |
---|---|
-u | 显示有差异行的前后几行(上下文), 默认是前后各 3 行, 这样, |
patch | 中带有更多的信息., |
-p | 显示代码所在的 c 函数的信息。 |
-r | 递归地对比一个目录和它的所有子目录(即整个目录树)。 |
-N | 如果某个文件缺少了, 就当作是空文件来对比. 如果不使用本选项, 当 diff 发现旧代码或者新代码缺少文件时, 只简单的提示缺少文件. 如果使用本选项, 会将新添加的文件全新打印出来作为新增的部分. |
patch 命令
patch 被用于为开放源代码软件安装补丁程序。让用户利用设置修补文件的方式,修改,更新原始文件。
使用 diff 命令生成的 patch.file 为 diff_A.txt 升级或者还原
[root@linux mnt]# patch diff_A.txt < path.file patching file diff_A.txt
[root@linux mnt]# patch -R diff_A.txt < path.file patching file diff_A.txt
tr 命令
tr 命令用于替换文本文件中的字符,格式为“tr [原始字符] [目标字符]”。
把某个文本内容中的英文全部替换为大写
[root@linux ~]# cat anaconda-ks.cfg | tr [a-z] [A-Z] #VERSION=DEVEL
# SYSTEM AUTHORIZATION INFORMATION AUTH --ENABLESHADOW --PASSALGO=SHA512
# USE CDROM INSTALLATION MEDIA CDROM
# USE GRAPHICAL INSTALL GRAPHICAL
# RUN THE SETUP AGENT ON FIRST BOOT FIRSTBOOT --ENABLE
IGNOREDISK --ONLY-USE=SDA # KEYBOARD LAYOUTS
KEYBOARD --VCKEYMAP=US --XLAYOUTS='US' # SYSTEM LANGUAGE
LANG EN_US.UTF-8
# NETWORK INFORMATION
NETWORK --BOOTPROTO=DHCP --DEVICE=ENO16777728 --ONBOOT=OFF -- IPV6=AUTO
NETWORK --HOSTNAME=linux.COM
# ROOT PASSWORD ROOTPW --ISCRYPTED
$6$HFRQIKOPAK5JQAWM$AVUYZ3JQZ4WAWCPLRHBPM08GNDZRBIVU2BG.RTM IXVZODSZK/PFUZJ8OKEWE0MDCX66OK3/BNYWKYGAIPGUFP.
# SYSTEM TIMEZONE
TIMEZONE AMERICA/NEW_YORK --ISUTC # SYSTEM BOOTLOADER CONFIGURATION
BOOTLOADER --APPEND=" CRASHKERNEL=AUTO" --LOCATION=MBR --BOOT- DRIVE=SDA
AUTOPART --TYPE=LVM
# PARTITION CLEARING INFORMATION CLEARPART --ALL --INITLABEL --DRIVES=SDA
%PACKAGES @^MINIMAL @CORE KEXEC-TOOLS
%END
%ADDON COM_REDHAT_KDUMP --ENABLE --RESERVE-MB='AUTO'
%END
Bash脚本
显示消息echo
[root@linux ~]# cat s2.sh
#!/bin/bash
#The examples of echo command
#
echo "The time is:" #字符串需要用''或""引起来
date echo "The who's logged on:"
who
[root@linux ~]# sh s2.sh
The time is:
Wed Oct 31 16:51:14 CST 2018
The who's logged on:
root tty1 2018-10-31 16:03
root pts/0 2018-10-31 16:02 (192.168.130.1)
使用变量
[root@linux ~]# cat s3.sh
#!/bin/bash
#The examples of '$'
#
echo "The user is: $USER" #echo中可以使用$变量
echo "The user id is: $UID"
echo "The user directory is: $HOME"
[root@linux ~]# sh s3.sh
The user is:root
The user id is:0
The user directory is:/root
用户变量
[root@linux ~]# cat s4.sh
#!/bin/bash
days=10
guest='alice'
echo "The user $guest logged system before $days days ago"
days=5
guest="tom"
echo "The user $guest logged system before $days days ago"
[root@linux ~]# sh s4.sh
The user alice logged system before 10 days ago
The user tom logged system before 5 days ago
命令替换
[root@linux ~]# cat s5.sh
#!/bin/bash
time=$(date +%F-%T)
cp -a /var/log/messages /tmp/messages_bak.$time
[root@linux ~]# sh s5.sh
[root@linux ~]# cd /tmp/
[root@linux tmp]# ls
messages_bak.2018-10-24-16:52:23
数学运算
[root@linux ~]# cat s6.sh
#!/bin/bash
var1=10
var2=20.23
result=$(echo "scale=2; $var1 + $var2" | bc)
echo The result is $result
[root@linux ~]# sh s6.sh
The result is 30.23
[root@linux ~]# cat s7.sh
#!/bin/bash
var1=10.46
var2=43.67
var3=33.2
var4=71
result=$(bc << EOF
scale = 4
a1 = ($var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
)
echo $result
[root@linux ~]# sh s7.sh
2813.9882
Bash条件测试
[root@linux ~]# cat s8.sh
#!/bin/bash
t_user=alice
if grep $t_user /etc/passwd
then
echo "The user name is:$USER"
echo "The user uid is:$UID:"
echo "Other dir info are:"
ls -a /home/$t_user/.b*
fi
[root@linux ~]# sh s8.sh
alice:x:10005:10008::/home/alice:/bin/bash
The user name is:root
The user uid is:0:
Other dir info are:
/home/alice/.bash_history /home/alice/.bash_profile
/home/alice/.bash_logout /home/alice/.bashrc
[root@linux ~]# cat s9.sh
#!/bin/bash
t_user=aaa
if grep $t_user /etc/passwd
then
echo "The user $t_user exists on system"
else
echo "The user $t_user does not exist on system."
fi
[root@linux ~]# sh s9.sh
The user aaa does not exist on system.
[root@linux ~]# cat s10.sh
#!/bin/bash
t_user=aaa
if grep $t_user /etc/passwd
then
echo "The user $t_user exists on system"
elif ls -d /home/$t_user
then
echo "The user $t_user does not exist on system."
echo "However, $t_user has a directory."
fi
[root@linux ~]# sh s10.sh
ls: cannot access /home/aaa: No such file or directory
[root@linux ~]# cat s101.sh #elif中嵌套
#!/bin/bash
t_user=aaa
if grep $t_user /etc/passwd
then
echo "The user $t_user exists on system"
elif ls -d /home/$t_user
then
echo "The user $t_user does not exist on system."
echo "However, $t_user has a directory."
else
echo "The user $t_user does not exist on system."
echo "And,$t_user does not have a directory."
fi
[root@linux ~]# sh s101.sh
ls: cannot access /home/aaa: No such file or directory
The user aaa does not exist on system.
And,aaa does not have a directory.
[root@linux ~]# cat s11.sh
#!/bin/bash
if [ -d $HOME ] && [ -w $HOME/testing ] #-d检查file是否存在且是一个目录,-w可写
then
echo "The file exists and you can write to it"
else
echo "I can not write to the file"
fi
[root@linux ~]# sh s11.sh
I can not write to the file
[root@linux ~]# cat s12.sh
#!/bin/bash
value1=10
value2=11
if [ $value1 -gt 5 ]
then
echo "The value $value1 is greater than 5"
fi
if [ $value1 -eq $value2 ]
then
echo "The values are equal"
else
echo "The values are different"
fi
[root@linux ~]# sh s12.sh
The value 10 is greater than 5
The values are different
Bash break、continue
一旦启动了循环,就必须等到循环完成所有的迭代?并不是这样,break continue就可控制循环
break
#break是退出循环的一个简单方法,用break命令来退出任意类型的循环,包括while和until循环
#跳出单个循环
[root@linux ~]# more break1.sh
#!/bin/bash
#
for var1 in 1 2 3 4 5 6 7 8 9 10
do
if [ $var1 -eq 5 ]
then
break
fi
echo "Iteration number: $var1"
done
echo "The for loop is completed"
[root@linux ~]# sh break1.sh
Iteration number: 1
Iteration number: 2
Iteration number: 3
Iteration number: 4
The for loop is completed
#跳出内部循环
[root@linux ~]# more break3.sh
#!/bin/bash
#
for (( a = 1; a < 4; a++ ))
do
echo "Outer loop: $a"
for (( b = 1; b < 100; b++ )) #内部循环里的for语句指明当变量b等于100时停
止迭代
do
if [ $b -eq 5 ] #但内部循环的if-then语句指明当变量b的值等于5时执行
break命令
then
break #在处理多个循环时,break命令会自动终止你所在的内层的循
环
fi
echo " Inner loop: $b"
done
done
[root@linux ~]# sh break3.sh
Outer loop: 1
Inner loop: 1
Inner loop: 2
Inner loop: 3
Inner loop: 4
Outer loop: 2
Inner loop: 1
Inner loop: 2
Inner loop: 3
Inner loop: 4
Outer loop: 3
Inner loop: 1
Inner loop: 2
Inner loop: 3
Inner loop: 4
#跳出外部循环
#break n;n指定了要跳出的循环层级,默认为1,表明跳出的是当前的循环,n为2,break命令就会停止下一级
[root@linux ~]# more break4.sh
#!/bin/bash
for (( a = 1; a < 4; a++ ))
do
echo "Outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -gt 4 ]
then
break 2
fi
echo " Inner loop: $b"
done
done
[root@linux ~]# sh break4.sh
Outer loop: 1
Inner loop: 1
Inner loop: 2
Inner loop: 3
Inner loop: 4
#continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环
[root@linux ~]# more continue1.sh
#!/bin/bash
#
for (( var1 = 1; var1 < 15; var1++ ))
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ] #当if-then语句的条件被满足时(值大
于5且小于10),shell会执行continue命令,跳过此次循环中剩余的命令,但整个循
环还会继续。
then
continue
fi
echo "Iteration number: $var1"
done
[root@linux ~]# sh continue1.sh
Iteration number: 1
Iteration number: 2
Iteration number: 3
Iteration number: 4
Iteration number: 5
Iteration number: 10
Iteration number: 11
Iteration number: 12
Iteration number: 13
Iteration number: 14
#while continue
#当shell执行continue命令时,它会跳过剩余的命令!
[root@linux ~]# more continue2.sh
#!/bin/bash
var1=0
while echo "while iteration: $var1"
[ $var1 -lt 6 ]
do
var1=$[ $var1 + 1 ]
if [ $var1 -gt 2 ] && [ $var1 -lt 5 ]
then
continue
fi
echo "这是测试"
done
[root@linux ~]# sh continue2.sh
while iteration: 0
这是测试
while iteration: 1
这是测试
while iteration: 2
while iteration: 3
while iteration: 4
这是测试
while iteration: 5
这是测试
while iteration: 6
#在if-then的条件成立前,一切都很正常,然后shell执行了continue命令。当shell执行continue命令时,它
跳过了while循环中余下的命令。不幸的是,被跳过的部分正是$var1计数变量增值的地方,而这个变量又被用于
while测试命令中。
[root@linux ~]# more continue3.sh
#!/bin/bash
# continuing an outer loop
for (( a = 1; a <= 5; a++ ))
do
echo "Iteration $a:"
for (( b = 1; b < 3; b++ ))
do
if [ $a -gt 2 ] && [ $a -lt 4 ]
then
continue 2 #continue命令来停止处理循环内的命令,但会继续处理外部循环
fi
var3=$[ $a * $b ]
echo " The result of $a * $b is $var3"
done
done
[root@linux ~]# sh continue3.sh
Iteration 1:
The result of 1 * 1 is 1
The result of 1 * 2 is 2
Iteration 2:
The result of 2 * 1 is 2
The result of 2 * 2 is 4
Iteration 3: #这里就是continue 2执行的结果
Iteration 4:
The result of 4 * 1 is 4
The result of 4 * 2 is 8
Iteration 5:
The result of 5 * 1 is 5
The result of 5 * 2 is 10
if-then-else
[root@linux ~]# more if_else1.sh
#!/bin/bash
#looking for a possible value
#
if [ $USER = "alice" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "root" ]
then
echo "Welcome $USER"
echo "Please enjoy your visit"
elif [ $USER = "david" ]
then
echo "Special testing account"
elif [ $USER = "mike" ]
then
echo "Do not forget to logout when you're done"
else
echo "Sorry, you are not allowed here"
fi
[root@linux ~]# sh if_else1.sh
Welcome root
Please enjoy your visit
[alice@linux ~]$ sh if_else1.sh
Welcome alice
Please enjoy your visit
[peter@linux ~]# sh if_else1.sh
Sorry, you are not allowed here
#elif语句继续if-then检查,为比较变量寻找特定的值
#有了case命令,就不需要再写出所有的elif语句来不停地检查同一个变量的值了
[root@linux ~]# more if_else2.sh
#!/bin/bash
# using the case command
#
case $USER in
root | alice)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
[root@linux ~]# sh if_else2.sh
Welcome, root
Please enjoy your visit
Bash用户输入、命令行参数、特殊参数、测试参数
命令行参数
#向s1脚本传递了两个参数10和20 脚本会通过特殊的变量来处理参数
#shell会将一些位置参数的 特殊变量输入到命令行中的所有参数
读取参数
#位置参数的标准表示,$0是程序名,$1是第一个参数,$2是第二个参数,依次类推,直到$9
[root@linux ~]# more mingling1.sh
#!/bin/bash
#
factorial=1
for (( number = 1; number <= $1 ; number++ ))
do
factorial=$[ $factorial * $number ]
done
echo The factorial of $1 is $factorial
[root@linux ~]# sh mingling1.sh 5
The factorial of 5 is 120
#多个命令参数
#如果需要输入更多的命令行参数,则每个参数都必须用空格分开
[root@linux ~]# more mingling2.sh
#
total=$[ $1 * $2 ]
echo The first parameter is $1.
echo The second parameter is $2.
echo The total value is $total.
[root@linux ~]# sh mingling2.sh 2 3
The first parameter is 2.
The second parameter is 3.
The total value is 6.
#如果脚本参数超过9个,仍然可以处理,需要修改变量名。在第9个变量后,在变量前加${10}
[root@linux ~]# more mingling3.sh
#!/bin/bash
#
total=$[ ${10} * ${11} ]
echo The tenth parameter is ${10}
echo The eleventh parameter is ${11}
echo The total is $total
[root@linux ~]# sh mingling3.sh 1 2 3 4 5 6 7 8 9 10 11
The tenth parameter is 10
The eleventh parameter is 11
The total is 110
#读取脚本名,用$0参数获取shell在命令行启动的脚本名
[root@linux ~]# more mingling4.sh
#!/bin/bash
#
echo The zero parameter is set to: $0
[root@linux ~]# sh mingling4.sh
The zero parameter is set to: mingling4.sh
[root@linux ~]# sh /root/mingling4.sh
The zero parameter is set to: /root/mingling4.sh
#basename,返回不包含路径的脚本
[root@linux ~]# more mingling5.sh
#!/bin/bash
#
name=$(basename $0)
echo
echo The script name is: $name
[root@linux ~]# sh mingling5.sh
The script name is: mingling5.sh
[root@linux ~]# sh /root/mingling5.sh
The script name is: mingling5.sh
测试参数
#使用参数前一定要检查其中是否存在数据
[root@linux ~]# more canshu7.sh
#!/bin/bash
#
if [ -n "$1" ] #-n检测$1中是否有数据
then
echo Hello $1, glad to meet you.
else
echo "Sorry, you did not identify yourself. "
fi
[root@linux ~]# sh canshu7.sh
Sorry, you did not identify yourself.
[root@linux ~]# sh canshu7.sh alice
Hello alice, glad to meet you.
特殊参数变量
#在bash shell中有些特殊变量,它们会记录命令行参数
#你可以统计一下命令行中输入了多少个参数,无需测试每个参数
#参数统计 $#
[root@linux ~]# more canshu8.sh
#!/bin/bash
#
echo There were $# parameters supplied.
[root@linux ~]# sh canshu8.sh
There were 0 parameters supplied.
[root@linux ~]# sh canshu8.sh 1 2 3 4 5
There were 5 parameters supplied.
[root@linux ~]# sh canshu8.sh apple orange
There were 2 parameters supplied.
[root@linux ~]# more canshu9.sh
#!/bin/bash
#
if [ $# -ne 2 ] #-ne测试命令行参数数量
then
echo
echo Usage: s9.sh a b
echo
else
total=$[ $1 + $2 ]
echo
echo The total is $total
echo
fi
[root@linux ~]# sh canshu9.sh 1 2
The total is 3
[root@linux ~]# sh canshu9.sh 18 9
The total is 27
#抓取所有的数据
#有时候需要抓取命令行上提供的所有参数。这时候不需要先用$#变量来判断命令行上有多少参数,然后再进行遍
历,你可以使用一组其他的特殊变量来解决这个问题
$*变量会将所有参数当成单个参数
$@变量会单独处理每个参数
[root@linux ~]# more canshu11.sh
#!/bin/bash
#
echo
count=1
#
for param in "$*" #用for命令遍历这两个特殊变量,你能看到它们是如何不同地处
理命令行参数的
do
echo "\$* Parameter #$count = $param"
count=$[ $count + 1 ]
done
#
echo
count=1
#
for param in "$@"
do
echo "\$@ Parameter #$count = $param"
count=$[ $count + 1 ]
done
[root@linux ~]# sh canshu11.sh alice jack john
$* Parameter #1 = alice jack john
$@ Parameter #1 = alice
$@ Parameter #2 = jack
$@ Parameter #3 = john
用户输入
#bash shell为此提供了read命令
#read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令会将数据放进一个变量
[root@linux ~]# more canshu12.sh
#!/bin/bash
#
echo -n "Enter your name: " #-n不会在字符串末尾换行
read name
echo "Hello $name, welcome to my program. "
[root@linux ~]# sh canshu12.sh
Enter your name: alice
Hello alice, welcome to my program.
#当有名字输入时,read命令会将姓和名保存在同一个变量中。read命令会将提示符后输入的所有数据分配给单个变量,要么你就指定多个变量
[root@linux ~]# more canshu14.sh
#!/bin/bash
#
read -p "Enter your name: " first last
echo "Checking data for $last, $first…"
[root@linux ~]# sh canshu14.sh
Enter your name: jack smith
Checking data for smith, jack…
#read -t 指定一个计时器 秒数
[root@linux ~]# more canshu16.sh
#!/bin/bash
#
if read -t 5 -p "Please enter your name: " name
then
echo "Hello $name, welcome to my script"
else
echo
echo "Sorry, too slow! "
fi
[root@linux ~]# sh canshu16.sh
Please enter your name:
Sorry, too slow! #五秒内未输入内容
[root@linux ~]# sh canshu16.sh
Please enter your name: a
Sorry, too slow! #五秒内未输入完毕
[root@linux ~]# sh canshu16.sh
Please enter your name: alice
Hello alice, welcome to my script #五秒内输入完成
#当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋给变量
[root@linux ~]# more canshu17.sh
#!/bin/bash
#
read -n1 -p "Do you want to continue [Y/N]? " answer #此处的参数“-n1”就表示输入的字符长度为一
case $answer in
Y | y) echo
echo "fine, continue on…";;
N | n) echo
echo OK, goodbye
exit;;
esac
[root@linux ~]# sh canshu17.sh
Do you want to continue [Y/N]? y #当输入一位字符时就直接判断输入的内容
fine, continue on…
[root@linux ~]# sh canshu17.sh
Do you want to continue [Y/N]? n #当输入一位字符时就直接判断输入的内容
OK, goodbye
#要隐藏的数据类型,如输入密码
[root@linux ~]# more canshu18.sh
#!/bin/bash
#
read -s -p "Enter your password: " pass
echo
echo "Is your password really $pass? "
[root@linux ~]# sh canshu18.sh
Enter your password:
Is your password really root?
[root@linux ~]# sh canshu18.sh
Enter your password:
Is your password really link?
#read读取linux文件中的文件
#while循环会持续通过read命令处理文件中的行,直到read命令以非零退出状态码退出
[root@linux ~]# more canshu19.sh
#!/bin/bash
#
count=1
cat /etc/passwd | while read line
do
echo "Line $count: $line"
count=$[ $count + 1]
done
echo "Finished processing the file"
[root@linux ~]# sh canshu19.sh
Line 1: root:x:0:0:root:/root:/bin/bash
Line 2: bin:x:1:1:bin:/bin:/sbin/nologin
Line 3: daemon:x:2:2:daemon:/sbin:/sbin/nologin
Line 4: adm:x:3:4:adm:/var/adm:/sbin/nologin
Line 5: lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
Line 6: sync:x:5:0:sync:/sbin:/bin/sync
Line 7: shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
Line 8: halt:x:7:0:halt:/sbin:/sbin/halt
Line 9: mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
Line 10: operator:x:11:0:operator:/root:/sbin/nologin
Line 11: games:x:12:100:games:/usr/games:/sbin/nologin
Line 12: ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
Line 13: nobody:x:99:99:Nobody:/:/sbin/nologin
Line 14: systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
Line 15: dbus:x:81:81:System message bus:/:/sbin/nologin
Line 16: polkitd:x:999:997:User for polkitd:/:/sbin/nologin
Line 17: postfix:x:89:89::/var/spool/postfix:/sbin/nologin
Line 18: sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
Line 19: tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
Line 20: student:x:1007:1007::/home/student:/bin/bash
Line 21: harry:x:1012:1012::/home/harry:/bin/bash
Line 22: alice:x:10005:10008::/home/alice:/bin/bash
Line 23: jack.willianmus:x:10006:10006::/home/jack:/bin/bash
Line 24: david:x:10007:10012::/home/david:/bin/bash
Line 25: peter:x:10008:10012::/home/peter:/bin/bash
Line 26: jack:x:10009:10013::/home/jack:/bin/bash
Line 27: mike:x:10010:10013::/home/mike:/bin/bash
Line 28: rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
Line 29: myquota1:x:10011:10025::/app/home/myquota1:/bin/bash
Line 30: myquota2:x:10012:10025::/app/home/myquota2:/bin/bash
Line 31: myquota3:x:10013:10025::/app/home/myquota3:/bin/bash
Line 32: myquota4:x:10014:10025::/app/home/myquota4:/bin/bash
Line 33: myquota5:x:10015:10025::/app/home/myquota5:/bin/bash
Line 34: apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
Line 35: dhcpd:x:177:177:DHCP server:/:/sbin/nologin
Finished processing the file
while循环会持续通过read命令处理文件中的行,直到read命令以非零退出状态码退出
基本的脚本函数
函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了(这个过程称为调用函数)
function name { #name属性定义了赋予函数的唯一名称
commands #commands是构成函数的一条或多条bash shell命令,按顺序执行
}
[root@linux ~]# more hanshu1.sh
#!/bin/bash
#
function func1 {
echo "This is an example of a function"
}
#
count=1
while [ $count -le 5 ]
do
func1 #每次引用函数名func1时,bash shell会找到func1函数的定义并执
行你在那里定义的命令
count=$[ $count + 1 ]
done
echo "This is the end of the loop"
func1
echo "Now this is the end of the script"
[root@linux ~]# sh hanshu1.sh
This is an example of a function
This is an example of a function
This is an example of a function
This is an example of a function
This is an example of a function
This is the end of the loop
This is an example of a function
Now this is the end of the script
#函数定义不一定非得是shell脚本中首先要做的事,但一定要小心。如果在函数被定义前使用函数,你会收到一条错误消息
[root@linux ~]# more hanshu2.sh
#!/bin/bash
#
count=1
echo "This line comes before the function definition"
#
function func1 {
echo "This is an example of a function"
}
while [ $count -le 5 ]
do
func1
count=$[ $count + 1 ]
done
echo "This is the end of the loop"
func2
echo "Now this is the end of the script"
function func2 { #由于func2函数还没有定义,脚本运行函数调用处时,产生了一
条错误消息
echo "This is an example of a function"
}
[root@linux ~]# sh hanshu2.sh
This line comes before the function definition
This is an example of a function
This is an example of a function
This is an example of a function
This is an example of a function
This is an example of a function
This is the end of the loop
hanshu2.sh: line 15: func2: command not found
Now this is the end of the script
#函数覆盖
[root@linux ~]# more hanshu3.sh
#!/bin/bash
#
function func1 {
echo "This is the first definition of the function name"
}
func1 #func1函数最初的定义工作正常,但重新定义该函数后,后续的函数调用都会
使用第二个定义
function func1 {
echo "This is a repeat of the same function name"
}
func1
echo "This is the end of the script"
[root@linux ~]# sh hanshu3.sh
This is the first definition of the function name
This is a repeat of the same function name
This is the end of the script
返回值
#函数的 退出状态码
#默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量 $? 来确定函数的退出状态码
[root@linux ~]# more hanshu4.sh
#!/bin/bash
#
func1() {
echo "trying to display a non-existent file"
ls -l badfile
}
echo "testing the function: "
func1
echo "The exit status is: $?"
[root@linux ~]# sh hanshu4.sh
testing the function:
trying to display a non-existent file
ls: cannot access badfile: No such file or directory
The exit status is: 2
#函数的退出状态码是2,这是因为函数中的最后一条命令没有成功运行。但你无法知道函数中其他命令中是否成功运行
[root@linux ~]# more hanshu5.sh
#!/bin/bash
# testing the exit status of a function
func1() {
ls -l badfile
echo "This was a test of a bad command"
}
echo "testing the function:"
func1
echo "The exit status is: $?"
[root@linux ~]# sh hanshu5.sh
testing the function:
ls: cannot access badfile: No such file or directory
This was a test of a bad command
The exit status is: 0
#由于函数最后一条语句echo运行成功,该函数的退出状态码就是0,尽管其中有一条命令并没有正常运行。使用函数的默认退出状态码是很危险的。
return
#bash shell使用return命令来退出函数并返回特定的退出状态码;return命令允许指定一个整数值来定义函数的退出状态码,从而提供了一种简单的途径来编程设定函数退出状态码
[root@linux ~]# more hanshu6.sh
#!/bin/bash
#
function dbl {
read -p "Enter a value: " value
echo "doubling the value"
return $[ $value * 2 ]
}
dbl
echo "The new value is $?"
[root@linux ~]# sh hanshu6.sh
Enter a value: 2
doubling the value
The new value is 4
[root@linux ~]# sh hanshu6.sh
Enter a value: 128
doubling the value
The new value is 0
[root@linux ~]# sh hanshu6.sh
Enter a value: 112
doubling the value
The new value is 224
#dbl函数会将$value变量中用户输入的值*2,然后用return命令返回结果。脚本用$?变量显示了该值
#函数一结束就取返回值;
#退出状态码必须是0~255
#如果在用$?变量提取函数返回值之前执行了其他命令,函数的返回值就会丢失。记住,$?变量会返回执行的最后一条命令的退出状态码
#返回值的取值范围。由于退出状态码必须小于256,函数的结果必须生成一个小于256的整数值。任何大于256的值都会产生一个错误值
使用函数输出
#和命令的输出保存到shell变量中一样,也可以对函数的输出采用同样的处理办法
[root@linux ~]# more hanshu7.sh
#!/bin/bash
#
function dbl {
read -p "Enter a value: " value
echo $[ $value * 2 ]
}
result=$(dbl) #将dbl函数的输出赋给$result变量
echo "The new value is $result"
[root@linux ~]# sh hanshu7.sh
Enter a value: 24
The new value is 48
[root@linux ~]# sh hanshu7.sh
Enter a value: 128
The new value is 256
[root@linux ~]# sh hanshu7.sh
Enter a value: 202
The new value is 404
函数中使用变量
#向函数传递参数
[root@linux ~]# more hanshu10.sh
#!/bin/bash
#
function func7 {
echo $[ $1 * $2 ]
}
if [ $# -eq 2 ]
then
value=$(func7 $1 $2)
echo "The result is $value"
else
echo "Usage: badtest1 a b"
fi
[root@linux ~]# sh hanshu10.sh
Usage: badtest1 a b
[root@linux ~]# sh hanshu10.sh 2 3
The result is 6
函数中的变量
#shell脚本麻烦的原因之一就是变量的作用域。作用域是变量可见的区域。函数中定义的变量与普通变量的作用域不同
#全局变量在shell脚本中任何地方都有效的变量。在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚本的主体部分读取它的值
#默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问
[root@linux ~]# more hanshu12.sh
#!/bin/bash
#
function func1 {
temp=$[ $value + 6 ]
result=$[ $temp * 2 ]
}
temp=4
value=6
func1
echo "The result is $result"
if [ $temp -gt $value ] #是那个temp?
then
echo "temp is larger"
else
echo "temp is smaller"
fi
[root@linux ~]# sh hanshu12.sh
The result is 24
temp is larger
#函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,要在变量声明的前面加上local关键字
数组变量和函数
#向函数传数组参数
#如果将数组变量当作单个参数传递的话,它不会起作用
[root@linux ~]# more hanshu14.sh
#!/bin/bash
#
function testit {
echo "The parameters are: $@"
thisarray=$1 #试图将该数组变量作为函数参数,函数只会取数组变量的第一个值
echo "The received array is ${thisarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
testit $myarray
[root@linux ~]# sh hanshu14.sh
The original array is: 1 2 3 4 5
The parameters are: 1
The received array is 1
#解决上个问题。将数组变量的值分解成单个的值,然后将这些值作为函数参数使用。在函数内部,可以将所有的参数重新组合成一个新的变量
[root@linux ~]# more hanshu15.sh
#!/bin/bash
#
function testit {
local newarray
newarray=(`echo "$@"`)
echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
testit ${myarray[*]}
[root@linux ~]# sh hanshu15.sh
The original array is 1 2 3 4 5
The new array value is: 1 2 3 4 5
#函数内部的累加
[root@linux ~]# more hanshu16.sh
#!/bin/bash
#
function addarray { #addarray函数会遍历所有的数组元素,将它们累加在一起
local sum=0
local newarray
newarray=($(echo "$@"))
for value in ${newarray[*]}
do
sum=$[ $sum + $value ]
done
echo $sum
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "The result is $result"
[root@linux ~]# sh hanshu16.sh
The original array is: 1 2 3 4 5
The result is 15
#从函数返回数组
[root@linux ~]# more hanshu17.sh
#!/bin/bash
#
function arraydblr {
local origarray
local newarray
local elements
local i
origarray=($(echo "$@"))
newarray=($(echo "$@"))
elements=$[ $# - 1 ]
for (( i = 0; i <= $elements; i++ ))
{
newarray[$i]=$[ ${origarray[$i]} * 2 ]
}
echo ${newarray[*]}
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=($(arraydblr $arg1))
echo "The new array is: ${result[*]}"
[root@linux ~]# sh hanshu17.sh
The original array is: 1 2 3 4 5
The new array is: 2 4 6 8 10
#用$arg1变量将数组值传给arraydblr函数。arraydblr函数将该数组重组到新的数组变量中,生成该输出数组变量的一个副本。然后对数据元素进行遍历,将每个元素值翻倍,并将结果存入函数中该数组变量的副本。
#arraydblr函数使用echo语句来输出每个数组元素的值。脚本用arraydblr函数的输出来重新生成一个新的数组变量
函数递归
#局部函数变量的一个特性是自成体系。除了从脚本命令行处获得的变量,自成体系的函数不需要使用任何外部资源。这个特性使得函数可以递归地调用
#函数可以调用自己来得到结果
[root@linux ~]# more hanshu18.sh
#!/bin/bash
# using recursion
function factorial {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$[ $1 - 1 ]
local result=$(factorial $temp)
echo $[ $result * $1 ]
fi
}
read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
[root@linux week5_work]# sh hanshu18.sh
Enter value: 1
The factorial of 1 is: 1
[root@linux week5_work]# sh hanshu18.sh
Enter value: 2
The factorial of 2 is: 2
[root@linux week5_work]# sh hanshu18.sh
Enter value: 3
The factorial of 3 is: 6
[root@linux week5_work]# sh hanshu18.sh
Enter value: 4
The factorial of 4 is: 24
[root@linux week5_work]# sh hanshu18.sh
Enter value: 5
The factorial of 5 is: 120
[root@linux week5_work]# sh hanshu18.sh
Enter value: 6
The factorial of 6 is: 720
#x! = x * (x-1)! 此函数是计算x的阶乘
Bash脚本数组定义声明及引用
数据结构,数据序列,保存了连续的多个数据,可以使用索引获取相关元素,相当于多个变量的集合
Bash支持一维数组(不支持多维数组),并且没有限定数组的大小 ;数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0
普通数组
[root@linux ~]# more test1
#!/bin/bash
array_t=(1 2 3 4) #命令行中定义并测试
array_t1=(1,2,3,4) #使用逗号则做为一个整体
echo $array_t #默认打印第一个下标的变量
echo ${array_t[2]}
echo ${array_t[-1]}
echo $array_t1
[root@linux ~]# sh test1
1
3
4
1,2,3,4
普通数组,自定义索引位置
[root@linux ~]# more test2
#!/bin/bash
array_t2[1]=a
array_t2[2]=b
array_t2[3]=c
array_t2[4]=d
echo ${array_t2[*]} #打印所有值
echo ${array_t2[@]} #也可用@符号
echo ${!array_t2[*]} #! 打印索引号,下标
echo ${!array_t2[@]} #也可用@符号
echo ${#array_t2[2]} #下标变量长度
echo ${#array_t2[*]} #显示数组中的元素个数(只统计值不为空的元素)
echo ${#array_t2[@]}
[root@linux ~]# sh test2
a b c d
a b c d
1 2 3 4
1 2 3 4
1
4
4
关联数组
关联数组支持字符串作为数组索引。使用关联数组必须先使用declare -A声明它
[root@linux ~]# more test3
#!/bin/bash
declare -A array_t3 # 声明之后就可以给其赋值了
array_t3=([name1]=westfile [name2]=linkpark)
echo ${array_t3[name1]} #name1 name2是关联数组的index
echo ${array_t3[name2]}
array_t3[name3]=u2 #也可分开赋值
array_t3[name4]=coldplay
echo ${array_t3[*]}
[root@linux ~]# sh test3
westfile
linkpark
u2 linkpark westfile coldplay
数组截取、替换
[root@linux ~]# more test4
#!/bin/bash
array_t4=(1 2 3 4 5 6)
echo ${array_t4[*]:2:2} #从数组全部元素中第2个元素向后截取2个元素出来
array_t41=${array_t1[*]:2:2} #截取并赋值
array_t42=${array_t1[*]/5/6} #数组中的5替换为6
array_t5=(onetwo three fourfive)
echo ${array_t5[*]#*o} #从左非贪婪匹配并删除所有数组变量中“*o”的内容
echo ${array_t5[*]##*o} #从左贪婪匹配并删除所有数组变量中“*o”的内容
echo ${array_t5[*]%*o} #从右非贪婪匹配并删除所有数组变量中“*o”的内容
echo ${array_t5[*]%%*o} #从右贪婪匹配并删除所有数组变量中“*o”的内容
[root@linux ~]# sh test4
3 4
netwo three urfive
three urfive
onetw three fourfive
three fourfive
for循环遍历数组
[root@linux ~]# more for1
#!/bin/bash
array_t6=1,2,3,4,5,6,7,8
for i in ${array_t6[*]};do #以数组值的方式直接遍历数组
echo $i
done
for ((i=0;i<${#array_t6[*]};i++));do # 以数组变量个数的方式遍历数组
echo ${array_t6[$i]}
done
for i in ${!array_t6[*]};do # 以数组index的方式遍历数组
echo ${array_t6[$i]}
done
[root@linux ~]# sh for1
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
1,2,3,4,5,6,7,8
Shell练习
Bash练习
1.显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大 小,保存脚本为sys_info.sh
#!/bin/bash
echo "System Device Info"
echo "------------------------"
var1=`hostname`
echo "localhost name:$var1"
var2=`ifconfig | grep 'inet' | grep -v 'inet6' | sed 's/^.*inet//g' | sed 's/netmask.*//g'`
echo "ip address:$var2"
var3=`cat /etc/redhat-release`
echo "Os info:$var3"
var4=`uname -a | cut -d' ' -f 3`
echo "Kernel info:$var4"
var5=`cat /proc/cpuinfo | grep "model name" | cut -d' ' -f 3,4,5,6,7,8`
echo "cpu info:$var5"
var6=`free -h | grep "Mem" | cut -d' ' -f12`
echo "Memory info:$var6"
var7=`fdisk -l | grep "Disk /dev/sda" | cut -d',' -f 1`
echo "Disk info:$var7"
2.将/etc/目录备份到/tmp下,并以此格式保存bak_etc_yyyy-mm-dd,保存为脚本bak_etc.sh
#!/bin/bash
cp -r /etc /tmp/bak_etc_`date +%F`
3.显示当前硬盘分区中空间利用率大的值,保存脚本为disk_used.sh
#!/bin/bash
disk_usage=`df |tr -s " " | cut -d' ' -f 5 | sort -n | tail -n1`
echo "The max disk used : $disk_usage"
4.显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序,保存脚本为link.sh
#!/bin/bash
netstat -tn | tr -s " " | cut -d " " -f 5| cut -d : -f 1 | sort -nr | uniq -c
5.计算/etc/passwd文件中的第5个用户和第15用户的ID之和,保存脚本为sum_id.sh
#!/bin/bash
var1=`head -n5 /etc/passwd | tail -n1 | cut -d ':' -f3`
var2=`head -n15 /etc/passwd | tail -n1 | cut -d ':' -f3`
let Num=$var1+$var2
echo $Num
6.统计/etc, /var, /usr目录中共有多少文件,保存脚本为sum.sh
#!/bin/bash
var1=`ls -l /etc/ | wc -l`
echo "/etc/:$var1"
var2=`ls -l /var/ | wc -l`
echo "/var/:$var2"
var3=`ls -l /usr/ | wc -l`
echo "/usr/:$var3"
let sum=$var1+$var2+$var3
echo "sum="$sum
7.接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则输出“该IP地址可访问”;如果不可 ping通,则输出“该IP地址不可访问”,保存脚本为ping.sh
#!/bin/bash
ipaddr='(\<([0-9]|[1-9][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))\>\.){3}\<([0-9]|[1-9][0-9]|1[0-5][1-9]|2([0-4][0-9]|25[0-5]))\>'
read -p "please input a IPV4 addr: " ipv4
if [[ $ipv4 =~ $ipaddr ]];then
ping $ipv4 -c 4 && echo "这个IP可以正常访问"||echo "这个IP不能访问"
else
echo "请输入正确的网址"
exit
fi