第 1 章 Shell 概述

Shell 是一个命令行解释器,它接收应用程序/用户命令,然后调用操作系统内核(Linux操作系统中有Shell和Linux内核)
image.png
Shell 还是一个功能相当强大的编程语言,易编写,易调试,灵活性强。
1)Linux提供的Shell解释器有

  1. ~ cat /etc/shells
  2. /bin/sh
  3. /bin/bash
  4. /usr/bin/sh
  5. /usr/bin/bash
  6. /usr/bin/tmux
  7. /bin/zsh

2)bash和sh的关系

  1. ~ ls -l /bin/sh
  2. lrwxrwxrwx. 1 root root 4 Oct 30 2020 /bin/sh -> bash
  3. ~ ls -l /bin/bash
  4. -rwxr-xr-x. 1 root root 964536 Apr 1 2020 /bin/bash

3)CentOS默认的解析器是bash

  1. ~ echo $BASH
  2. /bin/bash

第 2 章 Shell 脚本入门

1)脚本格式
脚本以 #!/bin/bash 开头(指定解析器)
2)第一个Shell脚本:helloworld.sh
(1)需求:创建一个Shell脚本,输出helloworld
(2)案例实操:

  1. ~ touch helloworld.sh
  2. ~ vim helloworld.sh
  3. # 在helloworld.sh中输入如下内容
  4. #!/bin/bash
  5. echo "helloworld"

(3)脚本的常用执行方式
第一种:采用 bash 或者 sh+ 脚本的相对路径或者绝对路径
sh+脚本的相对路径

  1. ~ sh ./helloworld.sh
  2. helloworld

sh+脚本的绝对路径

  1. ~ sh /root/helloworld.sh
  2. helloworld

bash脚本的相对路径

  1. ~ bash ./helloworld.sh
  2. helloworld

bash脚本的绝对路径

  1. ~ bash /root/helloworld.sh
  2. helloworld

第二种:采用输入脚本的绝对路径或者相对路径执行脚本(必须具有可执行权限 +x )
(1)首先要赋予 helloworld.sh 脚本的+x权限

  1. ~ chmod +x helloworld.sh

(2)执行脚本
相对路径

  1. ~ ./helloworld.sh
  2. helloworld

绝对路径

  1. ~ /root/helloworld.sh
  2. helloworld

注意: 第一种执行方法, 本质是 bash 解析器帮你执行脚本, 所以脚本本身不需要执行权限。 第二种执行方法, 本质是脚本需要自己执行, 所以需要执行权限。
【了解】第三种:在脚本的路径前面加上 “.” 或者 source
(1)有以下脚本

  1. ~ vim test.sh
  2. #!/bin/bash
  3. A=5
  4. echo $A

(2)分别使用 sh,bash,./ 和 . 的方式来执行,结果如下:

  1. ~ bash test.sh
  2. 5
  3. ~ echo $A
  4. ~ sh test.sh
  5. 5
  6. ~ echo $A
  7. ~ chmod +x test.sh
  8. ~ ./test.sh
  9. 5
  10. ~ echo $A
  11. ~ . test.sh
  12. 5
  13. ~ echo $A
  14. 5

原因:
前两种方式都是在当前 shell 中打开一个子 shell 来执行脚本内容, 当脚本内容结束, 则子 shell 关闭, 回到父 shell 中。
第三种, 也就是使用在脚本路径前加“.” 或者 source 的方式, 可以使脚本内容在当前shell 里执行, 而无需打开子 shell! 这也是为什么我们每次要修改完/etc/profile 文件以后, 需要 source 一下的原因。
开子 shell 与不开子 shell 的区别就在于, 环境变量的继承关系, 如在子 shell 中设置的当前变量, 父 shell 是不可见的。

第 3 章 变量

3.1 系统预定义变量

1)常用系统变量
$HOME、$PWD、$SHELL、$USER 等
2)案例实操
(1)查看系统变量的值

  1. ~ echo $HOME
  2. /root

(2)显示当前 Shell 中所有的变量:set

  1. ~ set
  2. BASH=/usr/bin/bash
  3. BASH_ALIASES=()
  4. BASH_ARGC=()
  5. BASH_ARGV=()
  6. BASH_CMDS=()
  7. BASH_LINENO=()
  8. BASH_SOURCE=()

3.2 自定义变量

1)基本语法
(1)定义变量:变量名=变量值,注意,= 号前后不能有空格
(2)撤销变量:unset 变量名
(3)声明静态比哪里:readonly 变量(declare -r),注意:不能使用 unset
2)变量定义规则
(1)变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写
(2)等号两侧不能有空格
(3)在 bash 中,变量默认类型都是字符串类型,无法直接进行数值运算
(4)变量的值如果有空格, 需要使用双引号或单引号括起来。
3)案例实操
(1)定义变量A

  1. ~ A=5
  2. ~ echo $A
  3. 5

(2)给变量 A 重新赋值

  1. ~ A=8
  2. ~ echo $A
  3. 8

(3)撤销变量A

  1. ~ unset A
  2. ~ echo $A

(4)声明静态的变量B=2,不能unset

  1. [root@kubesphere-docker ~]# readonly B=2
  2. [root@kubesphere-docker ~]# echo $B
  3. 2
  4. [root@kubesphere-docker ~]# B=9
  5. bash: B: readonly variable

(5)在 Bash 中,变量默认类型都是字符串类型,无法直接进行数值运算

  1. [root@kubesphere-docker ~]# C=1+2
  2. [root@kubesphere-docker ~]# echo $C
  3. 1+2
  4. [root@kubesphere-docker ~]# echo $[ C ]
  5. 3

(6)变量的值如果有空格, 需要使用双引号或单引号括起来

  1. [root@kubesphere-docker ~]# D=I love banzhang
  2. bash: love: command not found
  3. [root@kubesphere-docker ~]# D="I love banzhang"
  4. [root@kubesphere-docker ~]# echo $D
  5. I love banzhang

(7)可把变量提升为全局环境变量, 可供其他 Shell 程序使用

  1. export 变量名
  2. [root@kubesphere-docker ~]# vim helloworld.sh

在 helloworld.sh 文件中增加 echo $B

  1. [root@kubesphere-docker ~]# cat helloworld.sh
  2. #!/bin/bash
  3. echo "helloworld"
  4. echo $B

发现并没有打印输出变量 B 的值。

  1. [root@kubesphere-docker ~]# export B
  2. [root@kubesphere-docker ~]# bash helloworld.sh
  3. helloworld
  4. 2

子Shell 会继承 父Shell 的全局变量,子Shell 修改全局变量的值,并不会影响父Shell 的全局变量的值。

3.3 特殊变量

3.3.1 $n

1)基本语法
$n 位置参数(功能描述:n为数字,$0代表该脚本的名称,$1-$9代表第一到第九个参数,十以上的参数,十以上的参数需要用大括号包含, 如${10} )
2)案例实操

  1. ~ touch parameter.sh
  2. ~ vim parameter.sh
  3. #!/bin/bash
  4. echo '=======$n======='
  5. echo $0
  6. echo $1
  7. echo $2
  8. ~ chmod a+x parameter.sh
  9. ~ bash parameter.sh cls xz
  10. =======$n=======
  11. parameter.sh
  12. cls
  13. xz

3.3.2 $

1)基本语法
$# (功能描述:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性)
2)案例实操

  1. ~ vim parameter.sh
  2. #!/bin/bash
  3. echo '=======$n======='
  4. echo $0
  5. echo $1
  6. echo $2
  7. echo '=======$#======='
  8. echo $#
  9. ~ chmod a+x parameter.sh
  10. ~ bash parameter.sh cls xz
  11. =======$n=======
  12. parameter.sh
  13. cls
  14. xz
  15. =======$#=======
  16. 2

3.3.3 $* 、$@

1)基本语法
$ (功能描述: 这个变量代表命令行中所有的参数, $把所有的参数看成一个整体)
$@ (功能描述: 这个变量也代表命令行中所有的参数, 不过$@把每个参数区分对待)
2)案例实操

➜  ~ vim parameter.sh
#!/bin/bash
echo '=======$n======='
echo $0
echo $1
echo $2
echo '=======$#======='
echo $#
echo '=======$*======='
echo $*
echo '=======$@======='
echo $@

➜  ~ bash parameter.sh {a..e}
=======$n=======
parameter.sh
a
b
=======$#=======
5
=======$*=======
a b c d e
=======$@=======
a b c d e
➜  ~ vim parameter.sh 
#!/bin/bash
echo '=====$n====='
echo "script name:" $0
echo "1st paramater:" $1
echo "2nd paramater:" $2
echo 

echo '=====$#====='
echo "paramater number:" $#
echo 

echo '=====$*====='
echo "$*"
echo 

echo '=====$@====='
echo "$@"
echo

3.3.4 $?

1)基本语法
$? (功能描述: 最后一次执行的命令的返回状态。 如果这个变量的值为 0, 证明上一个命令正确执行; 如果这个变量的值为非 0(具体是哪个数, 由命令自己来决定) , 则证明上一个命令执行不正确了。 )
2)案例实操
判断 helloworld.sh 脚本是否正确执行

➜  ~ bash helloworld.sh
helloworld
➜  ~ echo $?
0

第 4 章 运算符

1)基本语法 :::info “$((运算式))” 或 “$[运算式] “
expr 运算符 ::: 2)案例实操
计算 (2+3) * 4的值

➜  ~ echo $[ (2+3)*4 ]
20

➜  ~ S=$(( (2+3)*4 ))
➜  ~ echo $S
20

第 5 章 条件判断

1)基本语法
(1)test condition
(2)[ condition ] (注意:condition 前后要有空格)
注意: 条件非空即为 true, [ atguigu ]返回 true, [ ] 返回 false。
2)常用判断条件
(1)两个整数之间比较 :::info

  • -eq 等于(equal)
  • -ne 不等于(not equal)
  • -lt 小于(less than)
  • -le 小于等于(less equal)
  • -gt 大于(greater than)
  • -ge 大于等于(greater equal) ::: 注意:如果是字符串之间的比较,用等号”=”判断相等;用 “!=” 判断不等。
    (2)按照文件权限进行判断

  • -r 有读的权限(read)

  • -w 有写的权限(write)
  • -x 有执行的权限(execute)

(3)按照文件类型进行判断

  • -e 文件存在(existence)
  • -f 文件存在并且是一个常规的文件(file)
  • -d 文件存在并且是一个目录(directory)

3)案例实操
(1)23 是否大于等于 22

➜  ~ [ 23 -ge 22 ]
➜  ~ echo $?
0

(2)helloworld 是否具有写权限

➜  ~ [ -w /root/helloworld.sh ]
➜  ~ echo $?
0

(3)/root/helloworld.sh 目录中的文件是否存在

➜  ~ [ -e /root/new_helloworld.sh ]
➜  ~ echo $?
1

(4) 多条件判断(&& 表示前一条命令执行成功时, 才执行后一条命令, || 表示上一条命令执行失败后, 才执行下一条命令)

➜  ~ [ kubesphere ] && echo OK || echo notOK
OK
➜  ~ [  ] && echo OK || echo notOK
notOK

➜  ~ a=15
➜  ~ [ $a -lt 20 ] && echo "$a < 20" || echo "$a > 20"
15 < 20

第 6 章 流程控制(重点)

6.1 if 判断

1)基本语法
(1)单分支

if [ 条件判断式 ];then
  程序
fi

或者

if [ 条件判断式 ]
then
  程序
fi

(2)多分支

if [ 条件判断式 ]
then
  程序
elif [ 条件判断式 ]
then
  程序
else
  程序
fi

注意事项:
(1)[ 条件判断式 ],中括号和条件判断之间必须有空格
(2)if 后要有空格
2)案例实操
输入一个数字, 如果是 1, 则输出 banzhang zhen shuai, 如果是 2, 则输出 cls zhen mei,如果是其它, 什么也不输出

➜  ~ touch if.sh
(1)➜  ~ vim if.sh
#!/bin/bash

[ $# -eq 0 ] && \
{ echo "Usage: if.sh <NUMBER:(1 or 2)>";exit; }

if [ $1 -eq 1 ] ;then
    echo "banzhang zhen shuai"
elif [ $1 -eq 2 ] ;then
    echo "cls zhen mei"
else
    echo "Please input 1 or 2"
fi

(2)➜  ~ vim if.sh
#!/bin/bash
if [ $1 -eq 1 ]
then
    echo "banzhang zhen shuai"
elif [ $1 -eq 2 ]
then
    echo "cls zhen mei"
fi

➜  ~ chmod 777 if.sh
➜  ~ bash if.sh 1
banzhang zhen shuai
➜  ~ bash if.sh 2
cls zhen mei

6.2 case 语句

1)基本语法

case $变量名 in
"值 1")
    如果变量的值等于值 1, 则执行程序 1
;;
"值 2")
    如果变量的值等于值 2, 则执行程序 2
;;
    …省略其他分支…
*)
    如果变量的值都不是以上的值, 则执行此程序
;;
esac

注意事项:
(1) case 行尾必须为单词“in” , 每一个模式匹配必须以右括号“) ” 结束。
(2) 双分号“;;” 表示命令序列结束, 相当于 java 中的 break。
(3) 最后的“*) ” 表示默认模式, 相当于 java 中的 default。
2)案例实操
输入一个数字

➜  ~ touch case.sh
(1)➜  ~ vim case.sh
#!/bin/bash

[ $# -eq 0 ] && \
{ echo "Usage: case.sh <NUMBER:(1 or 2 or other)>";exit; }

case $1 in
1)
    echo "banzhang"
;;
2)
    echo "cls"
;;
*)
    echo "renyao"
;;
esac

(2)➜  ~ vim case.sh
#!/bin/bash

[ $# -eq 0 ] && \
{ echo "Usage: case.sh <NUMBER:(1 or 2 or other)>";exit; }
case $1 in
"1")
    echo "banzhang"
;;
"2")
    echo "cls"
;;
*)
    echo "renyao"
;;
esac

➜  ~ bash case.sh  1
banzhang
➜  ~ bash case.sh  2
cls
➜  ~ bash case.sh  3
renyao

6.3 for 循环

1)基本语法1

for (( 初始值;循环控制条件;变量变化 ))
do
    程序
done

2)案例实操
从1加到100

➜  ~ touch for1.sh
➜  ~ vim for1.sh
#!/bin/bash

SUM=0
for (( i=0;i<=100;i++ ));do
    SUM=$[ $SUM+$i ]
done
echo $SUM

➜  ~ chmod 777 for1.sh
➜  ~ bash for1.sh
5050

1)基本语法2

for 变量 in 值 1 值 2 值 3…
do
    程序
done

2)案例实操
(1)打印所有输入参数

➜  ~ touch for2.sh
(1)➜  ~ vim for2.sh
#!/bin/bash
# 打印数字

for i in {cls,mly,wls}
do
    echo "ban zhang love $i"
done

➜  ~ chmod 777 for2.sh
➜  ~ bash for2.sh
ban zhang love cls
ban zhang love mly
ban zhang love wls

(2)➜  ~ vim for2.sh
#!/bin/bash
# 打印数字

for i in cls mly wls
do
    echo "ban zhang love $i"
done

(2)比较$ 和 $@ 的区别
$
和$@都表示传递给函数或脚本的所有参数, 不被双引号“”包含时, 都以$1 $2 …$n的形式输出所有参数。

➜  ~ touch for3.sh
➜  ~ vim for3.sh
#!/bin/bash

echo '=========$*========='
for i in $* ;do
    echo "ban zhang love $i"
done

echo '=========$@========='
for j in $@ ;do
    echo "ban zhang love $j"
done

➜  ~ chmod 777 for3.sh
➜  ~ bash for3.sh cls mly wls
=========$*=========
ban zhang love cls
ban zhang love mly
ban zhang love wls
=========$@=========
ban zhang love cls
ban zhang love mly
ban zhang love wls

当它们被双引号“”包含时, $*会将所有的参数作为一个整体, 以“$1 $2 …$n”的形式输出所有参数; $@会将各个参数分开, 以“$1” “$2”…“$n”的形式输出所有参数。

➜  ~ touch for4.sh
➜  ~ vim for4.sh
#!/bin/bash
echo '=======$*======='
for i in "$*" ;do
# $*中的所有参数看成是一个整体, 所以这个 for 循环只会循环一次
    echo "ban zhang love ${i}"
done

echo '=======$@======='
for j in "$@" ;do
# $@中的每个参数都看成是独立的, 所以“$@”中有几个参数, 就会循环几次
    echo "ban zhang love ${j}"
done

➜  ~ chmod 777 for3.sh
➜  ~ bash for4.sh cls mly wls
=======$*=======
ban zhang love cls mly wls
=======$@=======
ban zhang love cls
ban zhang love mly
ban zhang love wls

引号是视为字符串吧,$*是把写入的变量当成一整个字符串echo出来,而$@是把每一个输入的变量当成独立的echo出来

6.4 while 循环

1)基本语法

while [ 条件判断式 ]
do
    程序
done

2)案例实操
从1加到100

➜  ~ touch while.sh
➜  ~ vim while.sh
#!/bin/bash
SUM=0
i=1

while [ $i -le 100 ] ;do
    SUM=$[ $SUM+$i ]
    i=$[ $i+1 ]
done

echo $SUM

➜  ~ chmod 777 while.sh
➜  ~ bash while.sh
5050
➜  ~ vim while_test.sh 
#!/bin/bash

sum=0
i=1
while [ $i -le ${1} ]
do
        # sum=$[ $sum + $i ]
        # i=$[ $i+1 ]
        let sum+=i
        let i++
done
echo $sum

第 7 章 read 读取控制台输入

Linux read命令用于从标准输入读取数值。
read 内部命令被用来从标准输入读取单行数据。这个命令可以用来读取键盘输入,当使用重定向的时候,可以读取文件中的一行数据。
1)基本语法
read (选项)(参数)

(1)选项 -p:指定读取值时的提示符: -t:指定读取值时等待的时间(秒),如果-t 不加表示一直等待 (2)参数 变量:指定读取值的变量名

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]

  • -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
  • -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
  • -p 后面跟提示信息,即在输入前打印提示信息。
  • -e 在输入的时候可以使用命令补全功能。
  • -n 后跟一个数字,定义输入文本的长度,很实用。
  • -r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。
  • -s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
  • -t 后面跟秒数,定义输入字符的等待时间。
  • -u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的。

2)案例实操
提示 7秒内,读取控制台输入的名称:

➜  ~ touch read.sh
➜  ~ vim read.sh
➜  ~ chmod 777 read.sh
➜  ~ ./read.sh
Enter your name in 7 seconds : kubesphere
kubesphere

第 8 章 函数

8.1 系统函数

8.1.1 basename

1)基本语法 :::info basename [string / pathname] [suffix] ( 功能描述: basename 命令会删掉所有的前缀包括最后一个(‘/’) 字符, 然后将字符串显示出来。
basename 可以理解为取路径里的文件名称
选项:
suffix 为后缀, 如果 suffix 被指定了, basename 会将 pathname 或 string 中的 suffix 去掉。 ::: 2)案例实操
截取该 /home/xiaoming/helloworld.sh 路径的文件名称

➜  ~ basename /home/xiaoming/helloworld.sh
helloworld.sh
➜  ~ basename /home/xiaoming/helloworld.sh .sh
helloworld

8.1.2 dirname

1)基本语法 :::info dirname 文件绝对路径 (功能描述: 从给定的包含绝对路径的文件名中去除文件名(非目录的部分) , 然后返回剩下的路径(目录的部分) )
dirname 可以理解为取文件路径的绝对路径名称 ::: 2)案例实操
截取该 /home/xiaoming/helloworld.sh 路径

➜  ~ dirname /home/xiaoming/helloworld.sh
/home/xiaoming

8.2 自定义函数

1)基本语法 :::info [ function ] funname[()]
{
Action;
[return int;]
} ::: 2)经验技巧
(1) 必须在调用函数地方之前, 先声明函数, shell 脚本是逐行运行。 不会像其它语言一样先编译。
(2) 函数返回值, 只能通过$?系统变量获得, 可以显示加: return 返回, 如果不加, 将以最后一条命令运行结果, 作为返回值。 return 后跟数值 n(0-255)
3)案例实操
计算两个输入参数的和。

➜  ~ touch function.sh
➜  ~ vim function.sh
#!/bin/bash
function sum()
{
    sum=0
    sum=$[ $1+$2 ]
    echo "$sum";
}

read -p "Please input the number1:" n1
read -p "Please input the number2:" n2
sum $n1 $n2;

➜  ~ chmod 777 function.sh
➜  ~ bash function.sh
Please input the number1:1
Please input the number2:2
3
cat function_test.sh 
#!/bin/bash

function add() {
        sum=$[ ${1} + ${2} ]
        echo $sum
}

read -p "请输入第一个整数:" number1
read -p "请输入第二个整数:" number2

sum=$(add $number1 $number2)
echo "总和:" $sum
echo "总和的平方:"$[ $sum*$sum ]

第 9 章 正则表达式入门

正则表达式使用单个字符串来描述、 匹配一系列符合某个语法规则的字符串。 在很多文本编辑器里, 正则表达式通常被用来检索、 替换那些符合某个模式的文本。 在 Linux 中, grep,sed, awk 等文本处理工具都支持通过正则表达式进行模式匹配。

9.1 常规匹配

一串不包含特殊字符的正则表达式它自己,例如:

➜  ~ cat /etc/passwd | grep root
root:x:0:0:root:/root:/bin/zsh
operator:x:11:0:operator:/root:/sbin/nologin

就会匹配所有包含 root 的行

9.2 常用特殊字符

1)特殊字符 ^
^ 匹配一行的开头,例如:

➜  ~ cat /etc/passwd | grep "^a"

会匹配出所有以 a 开头的行
2)特殊字符 $
$ 匹配一行的结束,例如

➜  ~ cat /etc/passwd | grep "t$"

会匹配出所有以 t 结尾的行
思考:^$ 匹配的是什么? :::info ^$ 代表的是空行
➜ ~ cat /etc/profile | grep -Ev “^(#|$)”
过滤以 # 号开头和空行的行 ::: 3)特殊字符 .
. 匹配一个任意的字符,例如:

➜  ~ cat /etc/passwd | grep "r..t"

会匹配包含 rabt , rbbt , rxdt , root 等的所有行
4)特殊字符
不单独使用,它和上一个字符连用,表示匹配上一个字符 0次或者多次,例如:

➜  ~ cat /etc/passwd | grep "ro*t"

会匹配rt, rot, root, rooot, roooot 等所有行
思考:. 匹配的是什么? :::info . 匹配所有 ::: 5)特殊字符(中括号) [] :::info [ ] 表示匹配某个范围内的一个字符, 例如
[6,8]———匹配 6 或者 8
[0-9]———匹配一个 0-9 的数字
[0-9]———匹配任意长度的数字字符串
[a-z]———匹配一个 a-z 之间的字符
[a-z]
———匹配任意长度的字母字符串
[a-c, e-f]-匹配 a-c 或者 e-f 之间的任意字符 :::

➜  ~ cat /etc/passwd | grep "r[a,b,c]*t"

会匹配 rt,rat, rbt, rabt, rbact,rabccbaaacbt 等等所有行
6)特殊字符 \
\ 表示转义, 并不会单独使用。 由于所有特殊字符都有其特定匹配模式, 当我们想匹配某一特殊字符本身时(例如, 我想找出所有包含 ‘$’ 的行) , 就会碰到困难。 此时我们就要将转义字符和特殊字符连用, 来表示特殊字符本身, 例如

➜  ~ cat /etc/passwd | grep "a\$b"

就会匹配所有包含 a$b 的行。 注意需要使用单引号将表达式引起来。

第 10 章 文本处理工具

10.1 cut

cut 的工作就是“剪”, 具体的说就是在文件中负责剪切数据用的。 cut 命令从文件的每一行剪切字节、 字符和字段并将这些字节、 字符和字段输出
1)基本用法 :::info cut [选项参数] filename
说明:默认分隔符是制表符 ::: 2)选项参数说明

选项参数 功能
-f 列号, 提取第几列
-d 分隔符, 按照指定分隔符分割列, 默认是制表符“\t”
-c 按字符进行切割 后加加 n 表示取第几列 比如 -c 1

3)案例实操
(1)数据准备

➜  ~ vim cut.txt
dong shen zhong
guan zhen zhi
wo wo wei
lai lai
le le

(2)切割cut.txt第一列

➜  ~ cat cut.txt | cut -d" " -f1
➜  ~ cut -d" " -f1 cut.txt
dong
guan
wo
lai
le

(3)切割cut.txt第二、三列

➜  ~ cat cut.txt | cut -d" " -f2,3
➜  ~ cut -d" " -f2,3 cut.txt
shen zhong
zhen zhi
wo wei
lai
le

(4)在 cut.txt 文件中切割出 guan

➜  ~ cat cut.txt | grep guan | cut -d" " -f1
guan

(5)选取系统 PATH 变量值, 第 2 个“: ” 开始后的所有路径:

~ echo $PATH
/usr/local/apache/bin:/usr/local/sbin:[/usr/local/bin:/usr/sbin:/usr/bin:/root/bin]

~ echo $PATH | cut -d":" -f3-
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

(6) 切割 ifconfig 后打印的 IP 地址

~ ifconfig eth0 | grep netmask | tr -s " " : | cut -d":" -f3
10.150.22.47

~ ifconfig eth0 | grep netmask | cut -d" " -f10

10.2 awk

一个强大的文本分析工具, 把文件逐行的读入, 以空格为默认分隔符将每行切片, 切开的部分再进行分析处理。
1)基本用法 :::info awk [选项参数] ‘/pattern1/{action1} /pattern2/{action2}…’ filename
pattern: 表示 awk 在数据中查找的内容, 就是匹配模式
action: 在找到匹配内容时所执行的一系列命令 ::: 2)选项参数说明

选项参数 功能
-F 指定输入文件分隔符
-v 赋值一个用户定义变量

3)案例实操
(1)数据准备

~ cp /etc/passwd .
# passwd 数据的含义
# 用户名:密码(加密过后的):用户 id:组 id:注释:用户家目录:shell 解析器

(2)搜索passwd文件以 root 关键字开头的所有行,并输出该行的第7列

~ awk -F":" '/^root/{print $7}' passwd
/bin/zsh

(3)搜索 passwd 文件以 root 关键字开头的所有行, 并输出该行的第 1 列和第 7 列,中间以“,” 号分割。

~ awk -F":" '/^root/{print $1","$7}' /etc/passwd

注意: 只有匹配了 pattern 的行才会执行 action。
(4)只显示/etc/passwd 的第一列和第七列, 以逗号分割, 且在所有行前面添加列名 user,shell 在最后一行添加”dahaige, /bin/zuishuai”。

~ awk -F":" 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' /etc/passwd
user, shell
root,/bin/bash
......
etcd,/sbin/nologin
dahaige,/bin/zuishuai

注意: BEGIN 在所有数据读取行之前执行; END 在所有数据执行之后执行。
(5)将 passwd 文件中的用户 id 增加数值 1 并输出

~ awk -F":" -v"i=1" '{print $3+i}' passwd
~ awk -F":" '{print $3+1}' passwd

4)awk 的内置变量

变量 说明
FILENAME 文件名
NR 已读的记录数(行号)
NF 浏览记录的域的个数(切割后, 列的个数)

5)案例实操
(1)统计 passwd 文件名,每行的行号,每列的列数

~ awk -F":" '{print "filename:"FILENAME",linenum:"NR",col:"NF}' passwd
filename:passwd,linenum:1,col:7
filename:passwd,linenum:2,col:7
filename:passwd,linenum:3,col:7

(2)查看ifconfig 命令输出结果中的空行所在的行号

~ ifconfig | awk '/^$/{print NR}'
9
18
27

(3)切割IP

~ ifconfig eth0 | awk '/netmask/{print $2}'
10.150.22.47

第 11 章 综合应用案例

11.1 归档文件

实际生产应用中, 往往需要对重要数据进行归档备份。
需求: 实现一个每天对指定目录归档备份的脚本, 输入一个目录名称(末尾不带/) ,将目录下所有文件按天归档保存, 并将归档日期附加在归档文件名上, 放在/root/archive 下。
这里用到了归档命令: tar
后面可以加上-c 选项表示归档, 加上-z 选项表示同时进行压缩, 得到的文件后缀名为.tar.gz。
脚本实现如下:

➜  ~ vim tar_file.sh
#!/bin/bash

# 首先判断输入参数个数是否为1
# if [ $# -ne 1 ] ;then
#     echo "参数个数错误!应该输入一个参数,作为归档目录名"
#     exit
# fi

# 从参数中获取目录名称
read -p "请输入需要归档的目录名(末尾不带/):" DIR
if [ -d ${DIR} ] ;then
    echo
else 
    { echo -e "\n目录不存在!\n";exit; }
fi

DIR_NAME=$(basename $DIR)
DIR_PATH=$(cd $(dirname ${DIR});pwd )

# 获取当前的日期
DATE=$(date +%Y_%T)

# 定义生成的归档文件名称
FILE="archive_${DIR_NAME}_${DATE}.tar.gz"
DEST="/root/archive/${FILE}"

# 开始归档目录文件
echo "开始归档......"
echo
mkdir -p /root/archive
tar -zcvf ${DEST} ${DIR_PATH}/${DIR_NAME}

if [ $? -eq 0 ] ;then
    echo -e "\n归档成功!"
    echo -e "归档文件为:${DEST}.\n"
else
    echo -e "归档出现了问题!"
    echo
fi

exit

11.2 发送消息

我们可以利用 Linux 自带的 mesg 和 write 工具, 向其它用户发送消息。
需求: 实现一个向某个用户快速发送消息的脚本, 输入用户名作为第一个参数, 后面直接跟要发送的消息。 脚本需要检测用户是否登录在系统中、 是否打开消息功能, 以及当前发送消息是否为空。
脚本实现如下:

➜  ~ vim message.sh 
#!/bin/bash

# LOGIN_USER=$(who | awk '{print $1}' | uniq)

LOGIN_USER=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $LOGIN_USER ] ;then
    echo "$1 不在线"
    echo "脚本退出......"
    exit
fi

IS_ALLOWED=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $IS_ALLOWED != "+" ] ;then
    echo "$1 没有开启消息功能"
    echo "脚本退出......"
    exit
fi

WHOLE_MSG=$(echo $* | cut -d " " -f 2-)
USER_TERMINAL=$(who | grep -i -m 1 $1 | awk '{print $2}')

echo $WHOLE_MSG | write $LOGIN_USER $USER_TERMINAL

if [ $? != 0 ] ;then
    echo "发送失败!"
else 
    echo "发送成功!"
fi

exit
~ cat message_user.sh
#!/bin/bash

# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')

if [ -z $login_user ]
then
        echo "$1 不在线!"
        echo "脚本退出"
        exit
fi

# 查看用户是否开启消息功能
is_allowed=$(who -T| grep -i -m 1 $1 | awk '{print $2}')

if [ $is_allowed != "+" ]
then
        echo "$1 没有开启消息功能"
        echo "脚本退出"
        exit
fi

# 确认是否有消息发送
if [ -z $2 ]
then
        echo "没有消息发送"
        echo "脚本退出"
        exit
fi

# 从参数中获取要发送的消息
whole_msg=$(echo $* | cut -d " " -f 2-)

# 获取用户登录的终端
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')

# 写入要发送的消息
echo $whole_msg | write $login_user $user_terminal

if [ $? != 0 ]
then
        echo "发送失败!"
else
        echo "发送成功!"
fi

exit