每天一个新知识之Linux的Shell编程

28 篇文章 0 订阅

一、什么是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)
	格式化输出
	流程控制
  • awk的基本使用

[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 标准日期格式。
%gISO8610 标准周所在的年份模除 100(00-99)。比如,1993 年 1 月 1 日属于 1992 年的第 53 周。所以,虽然它是 1993 年第 1 天,但是其 ISO8601 标准周所在年份却是 1992。同样,尽管 1973 年 12 月 31 日属于 1973 年但是它却属于 1994 年的第一周。所以 1973 年 12 月 31 日的 ISO8610 标准周所在的年是 1974 而不是 1973。
%GISO 标准周所在年份的全称。
%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
  • awk的条件语句和循环语句

  1. 条件语句
    IF 语句

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语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

casein
值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!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值