一、什么是Shell
1、什么是Shell
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
2、shell的分类
常见的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)
从语法上划分主要语法类型有Bourne和C两种,这两种语法类型彼此不兼容。Bourne语法的shell主要包括:sh、ksh、Bash、psh、zsh;C语法的shell主要包括:csh、tcsh。我们可以通过cat /etc/shells来查看Linux支持的shell。
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
3、shell与shell 脚本(shell script)
shell脚本是一种为 shell 编写的脚本程序。
人们所说的 shell 通常都是指 shell 脚本,但shell 和 shell script 是两个不同的概念。
由于习惯的原因,简洁起见,本文出现的 “shell” 都是指 shell 脚本编程,不是指shell自身。
另外推荐个在线的shell测试工具,菜鸟教程在线Shell工具:
https://www.runoob.com/try/runcode.php?filename=helloworld&type=bash
4、sell脚本的执行方式
- 赋予脚本执行权限直接运行
- 通过bash调用执行
二、shell编程相关命令
1、echo
echo指令与 PHP 的 echo 指令类似,都是用于字符串的输出。
echo 字符串
2、printf
printf 命令模仿 C 程序库(library)里的 printf() 程序。
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
printf '输出类型输出格式' 输出内容
#输出类型:
%ns 输出字符串,n为输出n个字符
%ni 输出整数,n为输出n个整数
%m.nf 输出浮点数,m,n为整数,代表输出整数位和小数位位数
#输出格式
\a 输出警告声音
\b 输出退格键
\f 清屏
\n 换行
\r 回车
\t 水平输出tab
\V 垂直输出tab
#举例
[root@localhost disk_lv]# cat ptest.txt
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
printf '%s\t %s\t %s\t' $(cat ptest.txt)
姓名 性别 体重kg 郭靖 男 66.12 杨过 男 48.65 郭芙 女 47.99 [root@localhost disk_lv]#
[root@localhost disk_lv]# printf '%s\t %s\t %s\t \n' $(cat ptest.txt)
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
3、test
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
-
数值测试
参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt 大于则为真 -ge 大于等于则为真 -lt 小于则为真 -le 小于等于则为真 [root@localhost disk_lv]# cat test1.sh #!/bin/bash num1=100 num2=100 if test $[num1] -eq $[num2] then echo 'num1等于num2' else echo 'num1不等于num2' fi [root@localhost disk_lv]# chmod 755 test1.sh [root@localhost disk_lv]# ./test1.sh num1等于num2
-
字符串测试
参数 说明 = 等于则为真 != 不相等则为真 -z 字符串 字符串的长度为零则为真 -n 字符串 字符串的长度不为零则为真 [root@localhost disk_lv]# cat test2.sh #!/bin/bash num1='100' num2='100' if test $[num1]=$[num2] then echo 'num1与num2相等' else echo 'num1与num2不相等' fi [root@localhost disk_lv]# ./test2.sh num1与num2相等
-
文件测试
参数 说明 -e 文件名 如果文件存在则为真 -r 文件名 如果文件存在且可读则为真 -w 文件名 如果文件存在且可写则为真 -x 文件名 如果文件存在且可执行则为真 -s 文件名 如果文件存在且至少有一个字符则为真 -d 文件名 如果文件存在且为目录则为真 -f 文件名 如果文件存在且为普通文件则为真 -c 文件名 如果文件存在且为字符型特殊文件则为真 -b 文件名 如果文件存在且为块特殊文件则为真 [root@localhost disk_lv]# cat test3.sh #!/bin/bash if test -e test1.sh then echo 'test1存在' else echo 'test1不存在' fi [root@localhost disk_lv]# sh test3.sh test1存在
4、awk
AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。
awk '条件1{动作1} 条件2{动作2}...' 文件名
#条件(Pattern)
一般使用关系表达式作为条件。
#动作(Action)
格式化输出
流程控制
[root@localhost disk_lv]# cat ptest.txt
姓名 性别 体重kg
郭靖 男 66.12
杨过 男 48.65
黄蓉 女 99.99
郭芙 女 47.99
小龙女 女 49.88
尹志平 男 66.66
#输出第一列和第二列
[root@localhost disk_lv]# awk '{printf $1 "\t" $2 "\n"}' ptest.txt
姓名 性别
郭靖 男
杨过 男
黄蓉 女
郭芙 女
小龙女 女
尹志平 男
[root@localhost disk_lv]# df -h
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 475M 0 475M 0% /dev
tmpfs 487M 4.0K 487M 1% /dev/shm
tmpfs 487M 7.6M 479M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 20G 2.7G 18G 14% /
/dev/sdb1 20G 175M 19G 1% /app
/dev/sda1 197M 115M 82M 59% /boot
/dev/mapper/centos-home 2.0G 33M 2.0G 2% /home
tmpfs 98M 0 98M 0% /run/user/0
#输出已用空间
[root@localhost disk_lv]# df -h |awk '{print $1 "\t" $3}'
文件系统 已用
devtmpfs 0
tmpfs 4.0K
tmpfs 7.6M
tmpfs 0
/dev/mapper/centos-root 2.7G
/dev/sdb1 175M
/dev/sda1 115M
/dev/mapper/centos-home 33M
tmpfs 0
awk的运算符
运算符 | 描述 |
---|---|
= += -= *= /= %= ^= **= | 赋值 |
?: | C条件表达式 |
|| | 逻辑或 |
&& | 逻辑与 |
~ 和 !~ | 匹配正则表达式和不匹配正则表达式 |
< <= > >= != == | 关系运算符 |
空格 | 连接 |
+ - | 加,减 |
* / % | 乘,除与求余 |
+ - ! | 一元加,减和逻辑非 |
^ *** | 求幂 |
++ – | 增加或减少,作为前缀或后缀 |
$ | 字段引用 |
in | 数组成员 |
awk的内置变量
变量 | 描述 |
---|---|
$n | 当前记录的第n个字段,字段间由FS分隔 |
$0 | 完整的输入记录 |
ARGC | 命令行参数的数目 |
ARGIND | 命令行中当前文件的位置(从0开始算) |
ARGV | 包含命令行参数的数组 |
CONVFMT | 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组 |
ERRNO | 最后一个系统错误的描述 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔) |
FILENAME | 当前文件名 |
FNR | 各文件分别计数的行号 |
FS | 字段分隔符(默认是任何空格) |
IGNORECASE | 如果为真,则进行忽略大小写的匹配 |
NF | 一条记录的字段的数目 |
NR | 已经读出的记录数,就是行号,从1开始 |
OFMT | 数字的输出格式(默认值是%.6g) |
OFS | 输出记录分隔符(输出换行符),输出时用指定的符号代替换行符 |
ORS | 输出记录分隔符(默认值是一个换行符) |
RLENGTH | 由match函数所匹配的字符串的长度 |
RS | 记录分隔符(默认是一个换行符) |
RSTART | 由match函数所匹配的字符串的第一个位置 |
SUBSEP | 数组下标分隔符(默认值是/034) |
awk中两个特殊保留字BEGIN和END
BEGIN{ 这里面放的是执行前的语句 }
END {这里面放的是处理完所有的行后要执行的语句 }
vi score.txt
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
$ cat cal.awk
#!/bin/awk -f
#运行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
[root@localhost disk_lv]# awk -f cal.awk score.txt
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00
在awk中使用正则表达式
如果需要让awk识别正则表达式必须用/正则表达式/的方式,例如查看sdb系统分区的使用情况。
[root@localhost disk_lv]# df -h
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 475M 0 475M 0% /dev
tmpfs 487M 4.0K 487M 1% /dev/shm
tmpfs 487M 7.6M 479M 2% /run
tmpfs 487M 0 487M 0% /sys/fs/cgroup
/dev/mapper/centos-root 20G 2.7G 18G 14% /
/dev/sdb1 20G 175M 19G 1% /app
/dev/sda1 197M 115M 82M 59% /boot
/dev/mapper/centos-home 2.0G 33M 2.0G 2% /home
tmpfs 98M 0 98M 0% /run/user/0
[root@localhost disk_lv]# df -h | awk '/sda[0-9]/ {print $1 "\t" $5 "\n"}'
/dev/sda1 59%
awk的函数
算数函数
函数名 | 说明 | 实例 |
---|---|---|
atan2( y, x ) | 返回 y/x 的反正切。 | $ awk 'BEGIN { PI = 3.14159265 x = -10 y = 10 result = atan2 (y,x) * 180 / PI; printf "The arc tangent for (x=%f, y=%f) is %f degrees\n", x, y, result }' 输出结果为: The arc tangent for (x=-10.000000, y=10.000000) is 135.000000 degrees |
cos( x ) | 返回 x 的余弦;x 是弧度。 | $ awk 'BEGIN { PI = 3.14159265 param = 60 result = cos(param * PI / 180.0); printf "The cosine of %f degrees is %f.\n", param, result }' 输出结果为: The cosine of 60.000000 degrees is 0.500000. |
sin( x ) | 返回 x 的正弦;x 是弧度。 | $ awk 'BEGIN { PI = 3.14159265 param = 30.0 result = sin(param * PI /180) printf "The sine of %f degrees is %f.\n", param, result }' 输出结果为: The sine of 30.000000 degrees is 0.500000. |
exp( x ) | 返回 x 幂函数。 | $ awk 'BEGIN { param = 5 result = exp(param); printf "The exponential value of %f is %f.\n", param, result }' 输出结果为: The exponential value of 5.000000 is 148.413159. |
log( x ) | 返回 x 的自然对数。 | $ awk 'BEGIN { param = 5.5 result = log (param) printf "log(%f) = %f\n", param, result }' 输出结果为: log(5.500000) = 1.704748 |
sqrt( x ) | 返回 x 平方根。 | $ awk 'BEGIN { param = 1024.0 result = sqrt(param) printf "sqrt(%f) = %f\n", param, result }' 输出结果为: sqrt(1024.000000) = 32.000000 |
int( x ) | 返回 x 的截断至整数的值。 | $ awk 'BEGIN { param = 5.12345 result = int(param) print "Truncated value =", result }' 输出结果为: Truncated value = 5 |
rand( ) | 返回任意数字 n,其中 0 <= n < 1。 | $ awk 'BEGIN { print "Random num1 =" , rand() print "Random num2 =" , rand() print "Random num3 =" , rand() }' 输出结果为: Random num1 = 0.237788 Random num2 = 0.291066 Random num3 = 0.845814 |
srand( [Expr] ) | 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。 | $ awk 'BEGIN { param = 10 printf "srand() = %d\n", srand() printf "srand(%d) = %d\n", param, srand(param) }' 输出结果为: srand() = 1 srand(10) = 1417959587 |
字符串函数
函数 | 说明 | 实例 |
---|---|---|
gsub( Ere, Repl, [ In ] ) | gsub 是全局替换( global substitution )的缩写。除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。 | $ awk 'BEGIN { str = "Hello, World" print "String before replacement = " str gsub("World", "Jerry", str) print "String after replacement = " str }' 输出结果为: String before replacement = Hello, World String after replacement = Hello, Jerry |
sub(regex,sub,string) | sub 函数执行一次子串替换。它将第一次出现的子串用 regex 替换。第三个参数是可选的,默认为 $0。 | $ awk 'BEGIN { str = "Hello, World" print "String before replacement = " str sub("World", "Jerry", str) print "String after replacement = " str }' 输出结果为: String before replacement = Hello, World String after replacement = Hello, Jerry |
substr(str, start, l) | substr 函数返回 str 字符串中从第 start 个字符开始长度为 l 的子串。如果没有指定 l 的值,返回 str 从第 start 个字符开始的后缀子串。 | $ awk 'BEGIN { str = "Hello, World !!!" subs = substr(str, 1, 5) print "Substring = " subs }' 输出结果为: Substring = Hello |
index( String1, String2 ) | 在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。 | $ awk 'BEGIN { str = "One Two Three" subs = "Two" ret = index(str, subs) printf "Substring \"%s\" found at %d location.\n", subs, ret }' 输出结果为: Substring "Two" found at 5 location. |
length [(String)] | 返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 | $ awk 'BEGIN { str = "Hello, World !!!" print "Length = ", length(str) }' 输出结果为: Substring "Two" found at 5 location. |
blength [(String)] | 返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。 | |
substr( String, M, [ N ] ) | 返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。 | $ awk 'BEGIN { str = "Hello, World !!!" subs = substr(str, 1, 5) print "Substring = " subs }' 输出结果为: Substring = Hello |
match( String, Ere ) | 在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。 | $ awk 'BEGIN { str = "One Two Three" subs = "Two" ret = match(str, subs) printf "Substring \"%s\" found at %d location.\n", subs, ret }' 输出结果为: Substring "Two" found at 5 location. |
split( String, A, [Ere] ) | 将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。 | $ awk 'BEGIN { str = "One,Two,Three,Four" split(str, arr, ",") print "Array contains following values" for (i in arr) { print arr[i] } }' 输出结果为: Array contains following values One Two Three Four |
tolower( String ) | 返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 | $ awk 'BEGIN { str = "HELLO, WORLD !!!" print "Lowercase string = " tolower(str) }' 输出结果为: Lowercase string = hello, world !!! |
toupper( String ) | 返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。 | $ awk 'BEGIN { str = "hello, world !!!" print "Uppercase string = " toupper(str) }' 输出结果为: Uppercase string = HELLO, WORLD !!! |
sprintf(Format, Expr, Expr, . . . ) | 根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。 | $ awk 'BEGIN { str = sprintf("%s", "Hello, World !!!") print str }' 输出结果为: Hello, World !!! |
strtonum(str) | strtonum 将字符串 str 转换为数值。 如果字符串以 0 开始,则将其当作十进制数;如果字符串以 0x 或 0X 开始,则将其当作十六进制数;否则,将其当作浮点数。 | $ awk 'BEGIN { print "Decimal num = " strtonum("123") print "Octal num = " strtonum("0123") print "Hexadecimal num = " strtonum("0x123") }' 输出结果为: Decimal num = 123 Octal num = 83 Hexadecimal num = 291 |
时间函数
函数名 | 说明 | 实例 |
---|---|---|
mktime( YYYY MM DD HH MM SS[ DST]) | 生成时间格式 | $ awk 'BEGIN { print "Number of seconds since the Epoch = " mktime("2014 12 14 30 20 10") }' 输出结果为: Number of seconds since the Epoch = 1418604610 |
strftime([format [, timestamp]]) | 格式化时间输出,将时间戳转为时间字符串 具体格式,见下表. | $ awk 'BEGIN { print strftime("Time = %m/%d/%Y %H:%M:%S", systime()) }' 输出结果为: Time = 12/14/2014 22:08:42 |
systime() | 得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数 | awk 'BEGIN{now=systime();print now}' 输出结果为: 1343210982 |
日期和时间格式化
序号 | 描述 |
---|---|
%a | 星期缩写(Mon-Sun)。 |
%A | 星期全称(Monday-Sunday)。 |
%b | 月份缩写(Jan)。 |
%B | 月份全称(January)。 |
%c | 本地日期与时间。 |
%C | 年份中的世纪部分,其值为年份整除100。 |
%d | 十进制日期(01-31) |
%D | 等价于 %m/%d/%y. |
%e | 日期,如果只有一位数字则用空格补齐 |
%F | 等价于 %Y-%m-%d,这也是 ISO 8601 标准日期格式。 |
%g | ISO8610 标准周所在的年份模除 100(00-99)。比如,1993 年 1 月 1 日属于 1992 年的第 53 周。所以,虽然它是 1993 年第 1 天,但是其 ISO8601 标准周所在年份却是 1992。同样,尽管 1973 年 12 月 31 日属于 1973 年但是它却属于 1994 年的第一周。所以 1973 年 12 月 31 日的 ISO8610 标准周所在的年是 1974 而不是 1973。 |
%G | ISO 标准周所在年份的全称。 |
%h | 等价于 %b. |
%H | 用十进制表示的 24 小时格式的小时(00-23) |
%I | 用十进制表示的 12 小时格式的小时(00-12) |
%j | 一年中的第几天(001-366) |
%m | 月份(01-12) |
%M | 分钟数(00-59) |
%n | 换行符 (ASCII LF) |
%p | 十二进制表示法(AM/PM) |
%r | 十二进制表示法的时间(等价于 %I:%M:%S %p)。 |
%R | 等价于 %H:%M。 |
%S | 时间的秒数值(00-60) |
%t | 制表符 (tab) |
%T | 等价于 %H:%M:%S。 |
%u | 以数字表示的星期(1-7),1 表示星期一。 |
%U | 一年中的第几个星期(第一个星期天作为第一周的开始),00-53 |
%V | 一年中的第几个星期(第一个星期一作为第一周的开始),01-53。 |
%w | 以数字表示的星期(0-6),0表示星期日 。 |
%W | 十进制表示的一年中的第几个星期(第一个星期一作为第一周的开始),00-53。 |
%x | 本地日期表示 |
%X | 本地时间表示 |
%y | 年份模除 100。 |
%Y | 十进制表示的完整年份。 |
%z | 时区,表示格式为+HHMM(例如,格式要求生成的 RFC 822或者 RFC 1036 时间头) |
%Z | 时区名称或缩写,如果时区待定则无输出。 |
位操作函数
函数名 | 说明 | 实例 |
---|---|---|
and | 位与操作。 | $ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d AND %d) = %d\n", num1, num2, and(num1, num2) }' 输出结果为: (10 AND 6) = 2 |
compl | 按位求补。 | $ awk 'BEGIN { num1 = 10 printf "compl(%d) = %d\n", num1, compl(num1) }' 输出结果为: compl(10) = 9007199254740981 |
lshift | 左移位操作 | $ awk 'BEGIN { num1 = 10 printf "lshift(%d) by 1 = %d\n", num1, lshift(num1, 1) }' 输出结果为: lshift(10) by 1 = 20 |
rshift | 右移位操作 | $ awk 'BEGIN { num1 = 10 printf "rshift(%d) by 1 = %d\n", num1, rshift(num1, 1) }' 输出结果为: rshift(10) by 1 = 5 |
or | 按位或操作 | $ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d OR %d) = %d\n", num1, num2, or(num1, num2) }' 输出结果为: (10 OR 6) = 14 |
xor | 按位异或操作 | $ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d XOR %d) = %d\n", num1, num2, xor(num1, num2) }' 输出结果为: (10 bitwise xor 6) = 12 |
其他函数
函数名 | 说明 | 实例 |
---|---|---|
close(expr) | 关闭管道的文件 | $ awk 'BEGIN { cmd = "tr [a-z] [A-Z]" print "hello, world !!!" |& cmd close(cmd, "to") cmd |& getline out print out; close(cmd); }' 输出结果为: HELLO, WORLD !!! 第一条语句 cmd = “tr [a-z] [A-Z]” 在 AWK 中建立了一个双向的通信通道。 第二条语句 print 为 tr 命令提供输入。&| 表示双向通信。 第三条语句 close(cmd, “to”) 完成执行后关闭 to 进程。 第四条语句 cmd |& getline out 使用 getline 函数将输出存储到 out 变量中。 接下来的输出语句打印输出的内容,最后 close 函数关闭 cmd。 |
delete | 用于从数组中删除元素 | $ awk 'BEGIN { arr[0] = "One" arr[1] = "Two" arr[2] = "Three" arr[3] = "Four" print "Array elements before delete operation:" for (i in arr) { print arr[i] } delete arr[0] delete arr[1] print "Array elements after delete operation:" for (i in arr) { print arr[i] } }' 输出结果为: Array elements before delete operation: One Two Three Four Array elements after delete operation: Three Four |
exit | 终止脚本执行,它可以接受可选的参数 expr 传递 AWK 返回状态。 | $ awk 'BEGIN { print "Hello, World !!!" exit 10 print "AWK never executes this statement." }' 输出结果为: Hello, World !!! |
flush | 刷新打开文件或管道的缓冲区 | |
getline | 读入下一行 | 使用 getline 从文件 marks.txt 中读入一行并输出: $ awk '{getline; print $0}' marks.txt ,AWK 从文件 marks.txt 中读入一行存储到变量 0 中。在下一条语句中,我们使用 getline 读入下一行。因此AWK读入第二行并存储到 0 中。最后,AWK 使用 print 输出第二行的内容。这个过程一直到文件结束。 |
next | 停止处理当前记录,并且进入到下一条记录的处理过程。 | 当模式串匹配成功后程序并不执行任何操作: $ awk '{if ($0 ~/Shyam/) next; print $0}' marks.txt |
nextfile | 停止处理当前文件,从下一个文件第一个记录开始处理。 | 首先创建两个文件。 file1.txt 内容如下: file1:str1 file1:str2 file1:str3 file1:str4 文件 file2.txt 内容如下: file2:str1 file2:str2 file2:str3 file2:str4 现在我们来测试 nextfile 函数。 $ awk '{ if ($0 ~ /file1:str2/) nextfile; print $0 }' file1.txt file2.txt 输出结果为: file1:str1 file2:str1 file2:str2 file2:str3 file2:str4 |
return | 从用户自定义的函数中返回值。请注意,如果没有指定返回值,那么的返回值是未定义的。 | 创建文件 functions.awk,内容如下: function addition(num1, num2) { result = num1 + num2 return result } BEGIN { res = addition(10, 20) print "10 + 20 = " res } 执行该文件:$ awk -f functions.awk 10 + 20 = 30 |
system | 执行特定的命令然后返回其退出状态。返回值为 0 表示命令执行成功;非 0 表示命令执行失败。 | $ awk 'BEGIN { ret = system("date"); print "Return value = " ret }' 输出结果为: Sun Dec 21 23:16:07 IST 2014 Return value = 0 |
自定义函数
一个程序包含有多个功能,每个功能我们可以独立一个函数。
函数可以提高代码的复用性。
用户自定义函数的语法格式为:
function function_name(argument1, argument2, ...)
{
function body
}
function_name 是用户自定义函数的名称。函数名称应该以字母开头,其后可以是数字、字母或下划线的自由组合。AWK 保留的关键字不能作为用户自定义函数的名称。
自定义函数可以接受多个输入参数,这些参数之间通过逗号分隔。参数并不是必须的。我们也可以定义没有任何输入参数的函数。
function body 是函数体部分,它包含 AWK 程序代码。
# 返回最小值
function find_min(num1, num2)
{
if (num1 < num2)
return num1
return num2
}
# 返回最大值
function find_max(num1, num2)
{
if (num1 > num2)
return num1
return num2
}
# 主函数
function main(num1, num2)
{
# 查找最小值
result = find_min(10, 20)
print "Minimum =", result
# 查找最大值
result = find_max(10, 20)
print "Maximum =", result
}
# 脚本从这里开始执行
BEGIN {
main(10, 20)
}
$ awk -f functions.awk
Minimum = 10
Maximum = 20
IF 条件语句语法格式如下:
if (condition)
action
也可以使用花括号来执行一组操作:
if (condition)
{
action-1
action-1
.
.
action-n
}
以下实例用来判断数字是奇数还是偶数:
$ awk 'BEGIN {num = 10; if (num % 2 == 0) printf "%d 是偶数\n", num }'
输出结果为:
10 是偶数
IF - ELSE 语句
IF - ELSE 条件语句语法格式如下:
if (condition)
action-1
else
action-2
在条件语句 condition 为 true 时只需 action-1,否则执行 action-2。
$ awk 'BEGIN {
num = 11;
if (num % 2 == 0) printf "%d 是偶数\n", num;
else printf "%d 是奇数\n", num
}'
输出结果为:
11 是奇数
IF - ELSE - IF
我们可以创建多个 IF - ELSE 格式的判断语句来实现多个条件的判断:
$ awk 'BEGIN {
a=30;
if (a==10)
print "a = 10";
else if (a == 20)
print "a = 20";
else if (a == 30)
print "a = 30";
}'
输出结果为:
a = 30
循环
For
For 循环的语法如下:
for (initialisation; condition; increment/decrement)
action
for 语句首先执行初始化动作( initialisation ),然后再检查条件( condition )。如果条件为真,则执行动作( action ),然后执行递增( increment )或者递减( decrement )操作。只要条件为 true 循环就会一直执行。每次循环结束都会进条件检查,若条件为 false 则结束循环。
下面的例子使用 For 循环输出数字 1 至 5:
$ awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }'
输出结果为:
1
2
3
4
5
While
While 循环的语法如下:
while (condition)
action
While 循环首先检查条件 condition 是否为 true ,若条件为 true 则执行动作 action。此过程一直重复直到条件 condition 为 flase 才停止。
下面是使用 While 循环输出数字 1 到 5 的例子:
$ awk 'BEGIN {i = 1; while (i < 6) { print i; ++i } }'
输出结果为:
1
2
3
4
5
Break
break 用以结束循环:
在下面的示例子中,当计算的和大于 50 的时候使用 break 结束循环:
$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) break; else print "Sum =", sum
}
}'
输出结果为:
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
Continue
Continue 语句用于在循环体内部结束本次循环,从而直接进入下一次循环迭代。
下面的例子输出 1 到 20 之间的偶数:
$ awk 'BEGIN {for (i = 1; i <= 20; ++i) {if (i % 2 == 0) print i ; else continue} }'
输出结果为:
2
4
6
8
10
12
14
16
18
20
Exit
Exit 用于结束脚本程序的执行。
该函数接受一个整数作为参数表示 AWK 进程结束状态。 如果没有提供该参数,其默认状态为 0。
下面例子中当和大于 50 时结束 AWK 程序。
$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}'
输出结果为:
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
让我们检查一下脚本执行后的返回状态:
$ echo $?
执行上面的命令可以得到如下的结果:
19
三、shell的流程控制语句
if语句
if [判断条件]
then
执行命令...
fi
#判断nginx是否运行
if [ $(ps -ef | grep -c "nginx") -gt 1 ]; then echo "True"; fi
#判断/dev/sdb磁盘使用率,超过80%输出告警信息
#!/bin/bash
rate=$(df -h | grep "/dev/sdb" |awk '{print $5}' |cut -d "%" -f1)
if [ $rate -ge 80 ]
then
echo "警告!/dev/sdb使用率已超过80%"
fi
if else
if [判断条件]
then
条件为真时执行的命令
else
条件为假时执行的命令
fi
[root@localhost disk_lv]# dd if=/dev/zero of=/disk_lv/logs/test.log bs=1M count=120
记录了120+0 的读入
记录了120+0 的写出
125829120字节(126 MB)已复制,0.696068 秒,181 MB/秒
#判断logs目录是否大于100M如果大于则清空该目录,不大于输出信息
[root@localhost disk_lv]# cat auto_del_logs.sh
logs_size=$(du -h logs | awk '{ print $1 }' |cut -d "M" -f1)
if [ $logs_size -ge 100 ]
then
echo "/disk_lv/logs/目录大于100M准备清理..."
rm -rf /disk_lv/logs/*
echo "/disk_lv/logs清理完成!"
else
echo "目录大小小于100M无需清理!"
fi
[root@localhost disk_lv]# sh auto_del_logs.sh
/disk_lv/logs/目录大于100M准备清理...
/disk_lv/logs清理完成!
[root@localhost disk_lv]# dd if=/dev/zero of=/disk_lv/logs/test1.log bs=1M count=80
记录了80+0 的读入
记录了80+0 的写出
83886080字节(84 MB)已复制,0.0655851 秒,1.3 GB/秒
[root@localhost disk_lv]# sh auto_del_logs.sh
目录大小小于100M无需清理!
if elif else
if [判断条件1]
then
判断条件1成立执行的命令
elif [判断条件2]
then
判断条件2成立执行的命令
else
判断条件都不成立执行的命令
fi
#比较两个数的大小
a=10
b=20
if [ $a == $b ]
then
echo "a等于b!"
elif [ $a -gt $b ]
then
echo "a大于b!"
elif [ $a -lt $b ]
then
echo "a小于b!"
else
echo "没有符合的条件!"
fi
[root@localhost disk_lv]# sh ifelifelse.shs
a小于b!
for循环
#语法一
for 变量 in 值1,值2...
do
执行的命令
done
[root@localhost disk_lv]# cat for1.sh
for val in 1 2 3 4 5 6
do
echo $val
done
[root@localhost disk_lv]# sh for1.sh
1
2
3
4
5
6
#语法二
for ((循环开始条件;循环控制条件;变量变化量))
do
执行的命令
done
#计算1-100的和
[root@localhost disk_lv]# cat for2.sh
for (( i=1;i<=100;i++ ))
do
s=$(($s+$i))
done
echo $s
[root@localhost disk_lv]# sh for2.sh
5050
while循环
while [判断条件]
do
执行的命令
done
[root@localhost disk_lv]# cat while_1-100.sh
i=1
s=0
while [ $i -le 100 ]
do
s=$(($s+$i))
i=$(($i+1))
done
echo $s
[root@localhost disk_lv]# sh while_1-100.sh
5050
until循环
while与until的区别在于while是在判断条件为真时执行,而until则是在判断条件为假时执行。
until [判断条件]
do
执行的命令
done
[root@localhost disk_lv]# cat untile_1-5.sh
a=0
until [ ! $a -lt 5 ]
do
echo $a
a=`expr $a + 1`
done
[root@localhost disk_lv]# sh untile_1-5.sh
0
1
2
3
4
case语句
case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case 值 in
值1)
值为1时执行的命令
值2)
值为2时执行的命令
esac
[root@localhost disk_lv]# cat case_1.sh
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac
[root@localhost disk_lv]# sh ca
cal.awk case_1.sh
[root@localhost disk_lv]# sh ca
cal.awk case_1.sh
[root@localhost disk_lv]# sh case_1.sh
输入 1 到 4 之间的数字:
你输入的数字为:
1
你选择了 1
[root@localhost disk_lv]# sh case_1.sh
输入 1 到 4 之间的数字:
你输入的数字为:
5
你没有输入 1 到 4 之间的数字
break
break命令允许跳出所有循环(终止执行后面的所有循环。
continue
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
exit
在linux中exit命令用于推出当前登录的用户。但是在shell脚本中exit的作用是退出当前执行的脚本。
四、shell的函数
函数的定义格式
function 函数名 ()
{
函数内容;
[return 返回值]
}
函数的参数
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
函数的示例
[root@localhost disk_lv]# cat func1.sh
function func1 () {
echo "这是一个函数"
}
echo "开始调用函数"
func1
echo "函数调用完成"
[root@localhost disk_lv]# sh func1.sh
开始调用函数
这是一个函数
函数调用完成
[root@localhost disk_lv]# cat fun2.sh
sum(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
sum
echo "输入的两个数字之和为 $? !"
[root@localhost disk_lv]# sh fun2.sh
这个函数会对输入的两个数字进行相加运算...
输入第一个数字:
1
输入第二个数字:
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !
五、人生苦短,我用python
Shell的功能使用Python均可实现,而且代码量更少、结构更优雅、阅读性更好,而Python可实现的功能Shell却不一定能实现,如运维中会用到的用于网络通信的Socket模块、用于WEB的Django框架、用于性能采集的psutil模块等,而且Shell对操作系统的命令依赖性较强,Python可在更大程度上规避。Python的强大在于它是通用性语言,支持跨平台不像是shell仅能在linux和unix平台上运行,另外python的扩展性非常强如果需要一些特殊的功能安装以下模块就可以了。
对本人来说了解比较多且使用顺手的还是python经常用python来开发一些小的程序脚本和网站,对于shell来说不太熟悉且写起来感觉比python要难受很多,所以shell目前只作为了解,使用时还是偏向于python。
—人生苦短,我用python!