1 Shell 概述
1)需要看懂运维人员编写的 Shell 程序。
2)偶尔会编写一些简单 Shell 程序来管理集群、提高开发效率。
2 Shell 解析器
2.1 Shell 解析器
(1)Linux 提供的 Shell 解析器有:
1 | [root@localhost ~]$ cat /etc/shells |
sh/bash/csh/Tcsh/ksh/pdksh 等 shell 的区别
- sh(全称 Bourne Shell): 是 UNIX 最初使用的 shell,而且在每种 UNIX 上都可以使用。
- Bourne Shell 在 shell 编程方面相当优秀,但在处理与用户的交互方面做得不如其他几种 shell。
- bash(全称 Bourne Again Shell): LinuxOS 默认的,它是 Bourne Shell 的扩展。 与 Bourne Shell 完全兼容,并且在 Bourne Shell 的基础上增加了很多特性。可以提供命令补全,命令编辑和命令历史等功能。它还包含了很多 C Shell 和 Korn Shell 中的优点,有灵活和强大的编辑接口,同时又很友好的用户界面。
- csh(全称 C Shell): 是一种比 Bourne Shell 更适合的变种 Shell,它的语法与 C 语言很相似。
- Tcsh: 是 Linux 提供的 C Shell 的一个扩展版本。
- Tcsh 包括命令行编辑,可编程单词补全,拼写校正,历史命令替换,作业控制和类似 C 语言的语法,他不仅和 Bash Shell 提示符兼容,而且还提供比 Bash Shell 更多的提示符参数。
- ksh (全称 Korn Shell): 集合了 C Shell 和 Bourne Shell 的优点并且和 Bourne Shell 完全兼容。
- pdksh: 是 Linux 系统提供的 ksh 的扩展。
- pdksh 支持人物控制,可以在命令行上挂起,后台执行,唤醒或终止程序。
2.2 bash 和 sh 的关系
1 | [root@localhost bin]$ ll | grep bash |
2.3 默认解析器
Centos 默认的解析器是 bash
1 | [root@localhost bin]$ echo $SHELL |
3 Shell 脚本入门
3.1 脚本格式
脚本以 #!/bin/bash 开头(指定解析器)
当我们运行一个程序时,有时候会报错
/bin/bash^M: 坏的解释器: 没有那个文件或目录
原因在于是因为该文件在 windows 系统上打开过,关闭后其中的空格符号和 Linux 的不同,导致这个报错,我们可以通过 sed 命令与正则的配合将文件中的空格符号替换成 linux 的空格。
解决办法:
1 | #需要运行脚本全称为 test.sh |
3.2 单命令行脚本
需求:创建一个 Shell 脚本,输出 helloworld
案例实操:
1 | [root@localhost datas]$ touch helloworld.sh |
在 helloworld.sh 中输入如下内容
1 | #!/bin/bash |
3.3 脚本的常用执行方式
3.3.1 bash 或 sh+方式执行
采用 bash 或 sh+脚本的相对路径或绝对路径(不用赋予脚本+x 权限)
sh+脚本的相对路径
1 | [root@localhost datas]$ sh helloworld.sh |
输出结果为:
Helloworld
sh+脚本的绝对路径
1 | [root@localhost datas]$ sh /home/root/datas/helloworld.sh |
输出结果为:
helloworld
bash+脚本的相对路径
1 | [root@localhost datas]$ bash helloworld.sh |
输出结果为:
Helloworld
bash+脚本的绝对路径
1 | [root@localhost datas]$ bash /home/root/datas/helloworld.sh |
输出结果为:
Helloworld
3.3.2 脚本路径执行
采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限+x)
- 首先要赋予 helloworld.sh 脚本的+x 权限
1 | [root@localhost datas]$ chmod 777 helloworld.sh |
- 执行脚本
1 | 相对路径 |
注意:第一种执行方法,本质是 bash 解析器帮你执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
3.3 多命令行脚本
- 需求:
在/home/root/目录下创建一个 banzhang.txt,在 banzhang.txt 文件中增加“I love cls”。
- 案例实操:
1 | [root@localhost datas]$ touch batch.sh |
在 batch.sh 中输入如下内容
1 | #!/bin/bash |
3.4 Shell 输入/输出重定向
大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回 到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
重定向命令列表如下:
1 | command > file 将输出重定向到 file。 |
需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
具体方法参见 https://www.runoob.com/linux/linux-shell-io-redirections.html
输出重定向
重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:
1 | command1 > file1 |
注意任何 file1 内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
实例
执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
1 | $ who > users |
执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
你可以使用 cat 命令查看文件内容:
1 | $ cat users |
输出重定向会覆盖文件内容,请看下面的例子:
1 | $ echo "教程:www.demo.com" > users |
如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
1 | $ echo "教程:www.demo.com" >> users |
3.4.1 /dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
1 | $ command > /dev/null |
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出”的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
1 | $ command > /dev/null 2>&1 |
注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
3.4.2 &说明
1 | $ command > file 2>&1 |
这里的&没有固定的意思
放在>后面的&,表示重定向的目标不是一个文件,而是一个文件描述符,内置的文件描述符如下
1 | 1 => stdout |
换言之 2>1 代表将 stderr 重定向到当前路径下文件名为 1 的 regular file 中,而 2>&1 代表将 stderr 重定向到文件描述符为 1 的文件(即/dev/stdout)中,这个文件就是 stdout 在 file system 中的映射
而&>file 是一种特殊的用法,也可以写成>&file,二者的意思完全相同,都等价于
1 | >file 2>&1 |
此处&>或者>&视作整体,分开没有单独的含义
顺序问题
1 | find /etc -name .bashrc > list 2>&1 |
这个是从左到右有顺序的
第一种
1 | xxx > list 2>&1 |
先将要输出到 stdout 的内容重定向到文件,此时文件 list 就是这个程序的 stdout,再将 stderr 重定向到 stdout,也就是文件 list
第二种
1 | xxx 2>&1 > list |
先将要输出到 stderr 的内容重定向到 stdout,此时会产生一个 stdout 的拷贝,作为程序的 stderr,而程序原本要输出到 stdout 的内容,依然是对接在 stdout 原身上的,因此第二步重定向 stdout,对 stdout 的拷贝不产生任何影响
特别注意
对于上面 ‘2>&1’,举个例子,比如说:
1 | $ find /etc -names "*.txt" >list 2>&1 |
从左往右执行,执行到 >list,此时的 stdout 为 list;而执行到 2>&1,表示 stderr 重定向到 stdout,这里也就是 list 文件。
因为 [ find /etc -names “*.txt” ] 这条命令是错误的( -names 应该是 -name)。
本来要输出到终端屏幕的错误信息:
1 | find: unknown predicate `-names` |
被重定向到了 stdout 也就是 list 文件中,所以屏幕不会出现错误信息,而是打印到了 list 文件中。
cat list 可以查看到 find: unknown predicate `-names’ 就在里面。
4 Shell 中的变量
4.1 系统变量
4.1.1. 常用系统变量
$HOME、$PWD、$SHELL、$USER 等
4.1.2 案例实操
(1)查看系统变量的值
1 | [root@localhost datas]$ echo $HOME |
(2)显示当前 Shell 中所有变量:set
1 | [root@localhost datas]$ set |
4.1.3 Shell 字符串
字符串是 shell 编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟 PHP 类似。
单引号
1 | str='this is a string' |
单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
双引号
1 | your_name='demo' |
输出结果为:
Hello, I know you are “demo”!
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
拼接字符串
1 | your_name="demo" |
输出结果为:
1 | hello, demo ! hello, demo ! |
获取字符串长度
1 | string="abcd" |
提取子字符串
以下实例从字符串第 2 个字符开始截取 4 个字符:
1 | string="demo is a great site" |
查找子字符串
查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
1 | string="demo is a great site" |
注意:
以上脚本中 ` 是反引号,而不是单引号 ‘
4.1.4 字符串截取
假设有变量 var=http://www.aaa.com/123.htm
- # 号截取,删除左边字符,保留右边字符。
1 | echo ${var#*//} |
即删除 http:// , 结果是 :www.aaa.com/123.htm
- ## 号截取,删除左边字符,保留右边字符。
1 | echo ${var##*/} |
即删除 http://www.aaa.com/ , 结果是 123.htm
- %号截取,删除右边字符,保留左边字符
1 | echo ${var%/*} |
- %% 号截取,删除右边字符,保留左边字符
1 | echo ${var%%/*} |
结果是:http:
- 从左边第几个字符开始,及字符的个数
1 | echo ${var:0:5} |
结果是:http:
- 从左边第几个字符开始,一直到结束。
1 | echo ${var:7} |
结果是 :www.aaa.com/123.htm
- 从右边第几个字符开始,及字符的个数
1 | echo ${var:0-7:3} |
结果是:123
- 从右边第几个字符开始,一直到结束。
1 | echo ${var:0-7} |
结果是:123.htm
注:(左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示)
总结
、## 表示从左边开始删除。
- 一个 # 表示从左边删除到第一个指定的字符;
- 两个 # 表示从左边删除到最后一个指定的字符。
%、%% 表示从右边开始删除。
- 一个 % 表示从右边删除到第一个指定的字符;
- 两个 % 表示从左边删除到最后一个指定的字符。
删除包括了指定的字符本身。
示例
1 | #!bin/bash |
- echo “关于字符串的截取%,#的使用方法” echo “
- 原字符串为:”${var} echo “
- %%t*的效果:”${s1} echo “
- %t*的效果:”${s2} echo “
- %%.*的效果:”${s3} echo “
*/的效果:”${s4} echo “
*/的效果:”${s5}
运行结果:
1 | 关于字符串的截取%,#的使用方法 |
4.2 自定义变量
4.2.1 基本语法
- 定义变量:变量=值
- 撤销变量:unset 变量
- 声明静态变量:readonly 变量,注意:不能 unset
参数处理 | 说明 |
---|---|
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数 |
$$ | 脚本运行的当前进程 ID 号 |
$! | 后台运行的最后一个进程的 ID 号 |
$$ | 脚本运行的当前进程 ID 号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。 |
$- | 显示 Shell 使用的当前选项,与 set 命令功能相同。 |
$? | 显示最后命令的退出状态。0 表示没有错误,其他任何值表明有错误 |
4.2.2 变量定义规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
- 等号两侧不能有空格
- 在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算。
- 变量的值如果有空格,需要使用双引号或单引号括起来。
- 不能使用 bash 里的关键字(可用 help 命令查看保留关键字)
案例实操
(1)定义变量 A
1 | [root@localhost datas]$ A=5 |
(2)给变量 A 重新赋值
1 | [root@localhost datas]$ A=8 |
(3)撤销变量 A
1 | [root@localhost datas]$ unset A |
(4) s 声明静态变量
声明静态的变量 B=2,不能 unset
1 | [root@localhost datas]$ readonly B=2 |
(5)变量默认类型
在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算
1 | [root@localhost ~]$ C=1+2 |
输出结果为
1+2
(6)变量的值有空格
变量的值如果有空格,需要使用双引号或单引号括起来
1 | [root@localhost ~]$ D=I love banzhang |
输出结果为
I love banzhang
(7)变量提升为全局环境变量
可把变量提升为全局环境变量,可供其他 Shell 程序使用
export 变量名
1 | [root@localhost datas]$ vim helloworld.sh |
在 helloworld.sh 文件中增加 echo $B
1 | #!/bin/bash |
输出结果为
Helloworld
发现并没有打印输出变量 B 的值。
1 | [root@localhost datas]$ export B |
输出结果为
helloworld
2
除了显式地直接赋值,还可以用语句给变量赋值
1 | for file in `ls /etc` |
以上语句将 /etc 下目录的文件名循环出来。
4.3 特殊变量:$n
基本语法
1 | $n |
功能描述:n 为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含,如${10})
案例实操 : 输出该脚本文件名称、输入参数 1 和输入参数 2 的值
1 | #!/bin/bash |
执行脚本,输出结果如下所示:
1 | $ chmod +x test.sh |
4.4 特殊变量:$
基本语法
1 | $# |
(功能描述:获取所有输入参数个数,常用于循环)
案例实操 : 获取输入参数的个数
1 | [root@localhost datas]$ vim parameter.sh |
4.5 特殊变量:$*、$@
基本语法
1 | $* (功能描述:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体) |
案例实操 : 打印输入的所有参数
1 | [root@localhost datas]$ vim parameter.sh |
4.6 特殊变量:$?
基本语法
$? (功能描述:最后一次执行的命令的返回状态。如果这个变量的值为 0,证明上一个命令正确执行;如果这个变量的值为非 0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。)
案例实操 : 判断 helloworld.sh 脚本是否正确执行
1 | [root@localhost datas]$ ./helloworld.sh |
4.7 参数传递
在为 shell 脚本传递的参数中如果包含空格,应该使用单引号或者双引号将该参数括起来,以便于脚本将这个参数作为整体来接收。
在有参数时,可以使用对参数进行校验的方式处理以减少错误发生
1 | if [ -n "$1" ]; then |
注意
1 | 中括号 [] 与其中间的代码应该有空格隔开 |
shell 里面的中括号(包括单中括号与双中括号)可用于一些条件的测试
- 算术比较, 比如一个变量是否为 0, [ $var -eq 0 ]。
- 文件属性测试,比如一个文件是否存在,[ -e $var ], 是否是目录,[ -d $var ]。
- 字符串比较, 比如两个字符串是否相同, [[ $var1 = $var2 ]]。
-
5 运算符
5.1 基本语法
(1)“$((运算式))”或“$[运算式]”
(2)expr + , - , *, /, % 加,减,乘,除,取余
注意:expr 运算符间要有空格
5.2 案例实操
(1)计算 3+2 的值
1 | [root@localhost datas]$ expr 2 + 3 |
(2)计算 3-2 的值
1 | [root@localhost datas]$ expr 3 - 2 |
(3)计算(2+3)X4 的值
(a)expr 一步完成计算
1 | [root@localhost datas]$ expr `expr 2 + 3` \* 4 |
(b)采用$[运算式]方式
1 | [root@localhost datas]# S=$[(2+3)*4] |
注意
使用 expr 命令时,表达式中的运算符左右必须包含空格,如果不包含空格,将会输出表达式本身
1 | expr 5+6 // 直接输出 5+6 |
对于某些运算符,还需要我们使用符号”\”进行转义,否则就会提示语法错误。
1 | expr 5 * 6 // 输出错误 |
6 条件判断
6.1 基本语法
[ condition ](注意 condition 前后要有空格)
注意:条件非空即为 true,[ root ]返回 true,[] 返回 false。
6.2 常用判断条件
(1)两个整数之间比较
= 字符串比较
-lt 小于(less than)
-le 小于等于(less equal)
-eq 等于(equal)
-gt 大于(greater than)
-ge 大于等于(greater equal)
-ne 不等于(Not equal)
(2)按照文件权限进行判断
-r 有读的权限(read)
-w 有写的权限(write)
-x 有执行的权限(execute)
(3)按照文件类型进行判断
-f 文件存在并且是一个常规的文件(file)
-e 文件存在(existence)
-d 文件存在并是一个目录(directory)
6.3 案例实操
(1)23 是否大于等于 22
1 | [root@localhost datas]$ [ 23 -ge 22 ] |
(2)helloworld.sh 是否具有写权限
1 | [root@localhost datas]$ [ -w helloworld.sh ] |
(3)/home/root/cls.txt 目录中的文件是否存在
1 | [root@localhost datas]$ [ -e /home/root/cls.txt ] |
(4)多条件判断
(&& 表示前一条命令执行成功时,才执行后一条命令,|| 表示上一条命令执行失败后,才执行下一条命令)
1 | [root@localhost ~]$ [ condition ] && echo OK || echo notok |
6.4 Shell 中判断语句 if 中-z 至-d 的意思
1 | [ -a FILE ] 如果 FILE 存在则为真。 |
基本上和其他脚本语言一样。没有太大区别。不过值得注意的是。[]里面的条件判断。
1) 字符串判断
1 | str1 = str2 当两个串有相同内容、长度时为真 |
2) 数字的判断
1 | int1 -eq int2 两数相等为真 |
3) 文件的判断
1 | -r file 用户可读为真 |
4) 复杂逻辑判断
1 | -a 与 |
7 流程控制(重点)
7.1 if 判断
基本语法
1 | if [ 条件判断式 ];then |
注意事项:
(1)[ 条件判断式 ],中括号和条件判断式之间必须有空格
(2)if 后要有空格
案例实操
(1)输入一个数字,如果是 1,则输出 banzhang zhen shuai,如果是 2,则输出 cls zhen mei,如果是其它,什么也不输出。
1 | [root@localhost datas]$ touch if.sh |
7.2 case 语句
基本语法
1 | case $变量名 in |
注意事项
1)case 行尾必须为单词“in”,每一个模式匹配必须以右括号“)”结束。
2)双分号“;;”表示命令序列结束,相当于 java 中的 break。
3)最后的“*)”表示默认模式,相当于 java 中的 default。
案例实操
(1)输入一个数字,如果是 1,则输出 banzhang,如果是 2,则输出 cls,如果是其它,输出 renyao。
1 | [root@localhost datas]$ touch case.sh |
7.3 for 循环
7.3.1 基本语法 1
1 | for (( 初始值;循环控制条件;变量变化 )) |
案例实操
(1)从 1 加到 100
1 | [root@localhost datas]$ touch for1.sh |
7.3.2 基本语法 2
1 | for 变量 in 值1 值2 值3… |
案例实操
(1)打印所有输入参数
1 | [root@localhost datas]$ touch for2.sh |
7.3.3 比较$*和$@区别
$*和$@都表示传递给函数或脚本的所有参数,不被双引号“”包含时,都以$1 $2 …$n 的形式输出所有参数。
1 | [root@localhost datas]$ touch for.sh |
当它们被双引号“”包含时,“$*”会将所有的参数作为一个整体,以“$1 $2 …$n”的形式输出所有参数;“$@”会将各个参数分开,以“$1” “$2”…”$n”的形式输出所有参数。
1 | [root@localhost datas]$ vim for.sh |
7.4 while 循环
基本语法
1 | while [ 条件判断式 ] |
案例实操
(1)从 1 加到 100
1 | [root@localhost datas]$ touch while.sh |
8 read 读取控制台输入
基本语法
1 | read(选项)(参数) |
案例实操 : 提示 7 秒内,读取控制台输入的名称
1 | [root@localhost datas]$ touch read.sh |
以下实例读取键盘输入的内容并将其赋值给 shell 变量,为:-p 参数由于设置提示信息:
1 | read -p "input a val:" a #获取键盘输入的 a 变量数字 |
测试结果:
1 | input a val:1 |
9 函数
9.1 系统函数
9.1.1 basename 基本语法
1 | basename [string / pathname] [suffix] |
(功能描述:basename 命令会删掉所有的前缀包括最后一个(‘/’)字符,然后将字符串显示出来。
选项:
suffix 为后缀,如果 suffix 被指定了,basename 会将 pathname 或 string 中的 suffix 去掉。
案例实操 : 截取该/home/root/banzhang.txt 路径的文件名称
1 | [root@localhost datas]$ basename /home/root/banzhang.txt |
9.1.2 dirname 基本语法
1 | dirname 文件绝对路径 |
功能描述:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分))
案例实操 : 获取 banzhang.txt 文件的路径
1 | [root@localhost ~]$ dirname /home/root/banzhang.txt |
9.2 自定义函数
基本语法
1 | [ function ] funname[()] |
经验技巧
- 必须在调用函数地方之前,先声明函数,shell 脚本是逐行运行。不会像其它语言一样先编译。
- 函数返回值,只能通过$?系统变量获得,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。return 后跟数值 n(0-255)
- 可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数。
- 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 后跟数值 n(0-255
案例实操
(1)计算两个输入参数的和
1 | [root@localhost datas]$ touch fun.sh |
9.2.1 无参函数
下面的例子定义了一个函数并进行调用
1 | #!/bin/bash |
输出结果:
1 | -----函数开始执行----- |
9.2.2 带 return 函数
下面定义一个带有 return 语句的函数
1 | #!/bin/bash |
输出类似下面
1 | 这个函数会对输入的两个数字进行相加运算... |
函数返回值在调用该函数后通过 $? 来获得。
注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 shell 解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
9.2.3 函数参数
在 Shell 中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1 表示第一个参数,$2 表示第二个参数…
带参数的函数示例
1 | #!/bin/bash |
输出结果:
1 | 第一个参数为 1 ! |
注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当 n>=10 时,需要使用${n}来获取参数。
10 Shell 工具(重点)
10.1 cut
cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。
10.1.1 基本用法
1 | cut [选项参数] filename |
说明:默认分隔符是制表符
10.1.2 选项参数说明
选项参数 | 功能 |
---|---|
-f | 列号,提取第几列 |
-d | 分隔符,按照指定分隔符分割列 |
10.1.3 案例实操
(0)数据准备
1 | [root@localhost datas]$ touch cut.txt |
(1)切割 cut.txt 第一列
1 | [root@localhost datas]$ cut -d " " -f 1 cut.txt |
(2)切割 cut.txt 第二、三列
1 | [root@localhost datas]$ cut -d " " -f 2,3 cut.txt |
(3)在 cut.txt 文件中切割出 guan
1 | [root@localhost datas]$ cat cut.txt | grep "guan" | cut -d " " -f 1 |
(4)选取系统 PATH 变量值,第 2 个“:”开始后的所有路径:
1 | [root@localhost datas]$ echo $PATH |
(5)切割 ifconfig 后打印的 IP 地址
1 | [root@localhost datas]$ ifconfig eth0 | grep "inet addr" | cut -d: -f 2 | cut -d" " -f1 |
10.2 sed
sed 是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
10.2.1 基本用法
1 | sed [选项参数] ‘command’ filename |
选项参数说明
选项参数 | 功能 |
---|---|
-e | 直接在指令列模式上进行 sed 的动作编辑。 |
命令功能描述
1 | a 新增,a的后面可以接字串,在下一行出现 |
10.2.2 案例实操
(0)数据准备
1 | [root@localhost datas]$ touch sed.txt |
(1)将“mei nv”这个单词插入到 sed.txt 第二行下,打印。
1 | [root@localhost datas]$ sed '2a mei nv' sed.txt |
注意:文件并没有改变
(2)删除 sed.txt 文件所有包含 wo 的行
1 | [root@localhost datas]$ sed '/wo/d' sed.txt |
(3)将 sed.txt 文件中 wo 替换为 ni
1 | [root@localhost datas]$ sed 's/wo/ni/g' sed.txt |
注意:‘g’表示 global,全部替换
(4)将 sed.txt 文件中的第二行删除并将 wo 替换为 ni
1 | [root@localhost datas]$ sed -e '2d' -e 's/wo/ni/g' sed.txt |
10.3 awk
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
10.3.1 基本用法
1 | awk [选项参数] ‘pattern1{action1} pattern2{action2}...’ filename |
选项参数说明
1 | -F 指定输入文件折分隔符 |
10.3.2 案例实操
- 数据准备
1 | [root@localhost datas]$ sudo cp /etc/passwd ./ |
- 搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第 7 列。
1 | [root@localhost datas]$ awk -F: '/^root/{print $7}' passwd |
- 搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第 1 列和第 7 列,中间以“,”号分割。
1 | [root@localhost datas]$ awk -F: '/^root/{print $1","$7}' passwd |
注意:只有匹配了 pattern 的行才会执行 action
- 只显示/etc/passwd 的第一列和第七列,以逗号分割,且在所有行前面添加列名 user,shell 在最后一行添加”dahaige,/bin/zuishuai”。
1 | [root@localhost datas]$ awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd |
注意:BEGIN 在所有数据读取行之前执行;END 在所有数据执行之后执行。
- 将 passwd 文件中的用户 id 增加数值 1 并输出
1 | [root@localhost datas]$ awk -v i=1 -F: '{print $3+i}' passwd |
10.3.3 awk 的内置变量
1 | FILENAME 文件名 |
10.3.4 案例实操
- 统计 passwd 文件名,每行的行号,每行的列数
1 | [root@localhost datas]$ awk -F: '{print "filename:" FILENAME ", linenumber:" NR ",columns:" NF}' passwd |
- 切割 IP
1 | [root@localhost datas]$ ifconfig eth0 | grep "inet addr" | awk -F: '{print $2}' | awk -F " " '{print $1}' |
- 查询 sed.txt 中空行所在的行号
1 | [root@localhost datas]$ awk '/^$/{print NR}' sed.txt |
10.4 sort
sort 命令是在 Linux 里非常有用,它将文件进行排序,并将排序结果标准输出。
10.4.1 基本语法
1 | sort(选项)(参数) |
参数:指定待排序的文件列表
10.4.2 案例实操
- 数据准备
1 | [root@localhost datas]$ touch sort.sh |
- 按照“:”分割后的第三列倒序排序。
1 | [root@localhost datas]$ sort -t : -nrk 3 sort.sh |
第 11 章 企业真实面试题
11.1 京东
问题 1:使用 Linux 命令查询 file1 中空行所在的行号
答案:
1 | [root@localhost datas]$ awk '/^$/{print NR}' sed.txt |
问题 2:有文件 chengji.txt 内容如下:
1 | 张三 40 |
使用 Linux 命令计算第二列的和并输出
1 | [root@localhost datas]$ cat chengji.txt | awk -F " " '{sum+=$2} END{print sum}' |
11.2 搜狐&和讯网
问题 1:Shell 脚本里如何检查一个文件是否存在?如果不存在该如何处理?
1 | #!/bin/bash |
11.3 新浪
问题 1:用 shell 写一个脚本,对文本中无序的一列数字排序
1 | [root@localhost ~]# cat test.txt |
1 | [root@localhost ~]# sort -n test.txt|awk '{a+=$0;print $0}END{print "SUM="a}' |
11.4 金和网络
问题 1:请用 shell 脚本写出查找当前文件夹(/home)下所有的文本文件内容中包含有字符”shen”的文件名称
1 | [root@localhost datas]$ grep -r "shen" /home | cut -d ":" -f 1 |
本文参考列表