Linux零基础Shell教学全集(可用于日常查询语句,目录清晰,内容详细)(自学尚硅谷B站shell课程后的万字学习笔记,附课程链接)

发布于:2025-07-31 ⋅ 阅读:(38) ⋅ 点赞:(0)

此文章为学习了 尚硅谷B站课程 后的学习笔记

【尚硅谷】Shell脚本从入门到实战_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1hW41167NW/?spm_id_from=333.337.search-card.all.click&vd_source=68e0bbe20c8b1102b59ced40f67db628注意:需要先学Linux基础命令后,学习shell命令

Linux常用基础命令-CSDN博客https://blog.csdn.net/2302_78022640/article/details/149337415?spm=1011.2415.3001.5331

零基础 Shell 教学全集:全面入门、精讲实例、实战脚本


第 1 章:什么是Shell

Shell 是一个命令行解释器

Shell 是 用户与 Linux 内核交互的核心接口,兼具 命令解释器 和 脚本编程语言 的双重身份,也可理解为包裹内核的 “保护壳”(隔离用户直接操作内核,保障系统安全)。以下从核心逻辑、功能特性、常见版本展开解析:

核心作用:连接用户与内核

  • 命令中转:接收用户输入的指令(如 ls 查看文件、cd 切换目录),将其转换为内核能理解的格式,驱动内核执行操作,再把结果反馈给用户。
  • 安全隔离:用户无法直接访问内核,必须通过 Shell 交互,避免误操作破坏系统核心,就像 “门卫” 守护内核。

1.1 常见 Shell 解析器

Linux 系统提供了多种 Shell 解析器,我们可以通过以下命令查看系统中可用的 Shell 解析器:

cat /etc/shells

输出可能为:

/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh

这表示当前系统中安装了这些 Shell 解析器,其中 bash 是最常用的默认的一种。

1.2 bash 和 sh 的关系

在很多 Linux 系统中,sh 实际上是 bash 的一个链接(符号链接),我们可以通过以下命令查看它们的关系:

(本文章第九章有说明grep的使用,正则表达式)

ll | grep bash

含义是:

  • ll:是 ls -l 的别名,列出当前目录下的文件详细信息(包括权限、所有者、大小、修改时间等)。

  • grep bash:从 ll 的输出中筛选出包含 “bash” 的行。

输出:

这里的 lrwxrwxrwx 表示 sh 是一个符号链接,箭头 -> 后面的 bash 表示它链接到 bash,这意味着执行 sh 命令实际上等同于执行 bash 命令。

1.3 查看当前默认 shell

要查看当前系统默认使用的 Shell 解析器,可以使用以下命令:

echo $SHELL

在 CentOS 系统中,输出通常为:

/bin/bash

这表明 CentOS 默认的 Shell 解析器是 bash


第 2 章:Shell 脚本入门

2.1 脚本格式要求

编写 Shell 脚本时,有一个固定的格式要求:脚本必须以 #!/bin/bash 开头,这一行的作用是指定脚本使用 bash 解析器来执行。同时,为了便于识别,Shell 脚本通常使用 .sh 作为后缀名。

可以先建一个文件夹:

mkdir codes
cd codes

如图例:

2.2 Hello World 示例

下面我们来编写第一个 Shell 脚本,实现输出 "helloworld" 的功能。

步骤:

  1. 创建脚本文件:
    touch helloworld.sh
    
  2. 编辑脚本内容:
    vim helloworld.sh
    
    (打开后按 “i” 开始编写,写完后按 “Esc” ,再输入 “:wq” 出来)(下面这步不会的请先学linux基础命令)
  3. 在打开的文件中输入以下内容:
    #!/bin/bash
    echo "helloworld"
    

2.3 三种执行方式

Shell 脚本有多种执行方式,不同的方式有不同的特点和使用场景。

第一种:不需要执行权限(bash/sh + 路径)

这种方式是通过 bash 或 sh 命令来执行脚本,不需要给脚本赋予可执行权限。可以使用相对路径或绝对路径指定脚本位置:

  • 相对路径(在脚本所在目录执行)(cd 转目录):
    sh ./helloworld.sh
    bash ./helloworld.sh
    
  • 绝对路径(可以在任意目录执行):
    sh /home/atguigu/shells/helloworld.sh
    bash /home/atguigu/shells/helloworld.sh
    

这种方式的本质是 bash 或 sh 解析器帮我们执行脚本,所以脚本本身不需要可执行权限。

第二种:需要执行权限(直接执行)

这种方式是直接执行脚本文件,需要先给脚本赋予可执行权限:

(可以看到什么图片中helloworld.sh 文件被加粗了)

  1. 赋予权限(此为linux基础命令):
    chmod +x helloworld.sh
    
  2. 执行脚本:
    • 相对路径(在脚本所在目录执行):
      ./helloworld.sh
      
    • 绝对路径(可以在任意目录执行):
      /home/atguigu/shells/helloworld.sh
      

这种方式的本质是脚本自己执行,所以必须要有可执行权限。

第三种:使用 source 或 .(当前 shell 执行)

使用 source 命令或 .(点号)执行脚本,脚本会在当前 Shell 环境中执行,而不是创建一个新的子 Shell 环境。这会导致脚本中定义的变量在脚本执行后仍然有效。

例如,有一个 test.sh 脚本:

cat test.sh

内容如下:

#!/bin/bash
A=5
echo $A

分别用不同方式执行:

bash test.sh
sh test.sh
./test.sh
. test.sh  # 或 source test.sh
echo $A    # 只有 source 执行才能打印 5

前三种方式(bashsh、直接执行)都会创建子 Shell 来执行脚本,脚本执行结束后子 Shell 关闭,在子 Shell 中定义的变量不会影响父 Shell;而使用 source 或 . 执行脚本时,脚本在当前 Shell 中执行,变量会保留在当前 Shell 中。


第 3 章:变量

在 Shell 中,变量是存储数据的容器,我们可以通过变量名来访问存储的数据。Shell 中的变量分为系统预定义变量、自定义变量和特殊变量。

3.1 系统变量示例

系统预定义变量是系统自带的变量,用于存储系统相关的信息。常见的系统变量有:

  • $HOME:当前用户的家目录
  • $PWD:当前工作目录
  • $SHELL:当前默认的 Shell 解析器
  • $USER:当前登录的用户

可以通过 echo 命令查看这些变量的值:

echo $HOME
echo $PWD
echo $SHELL
echo $USER

使用 set 命令可以显示当前 Shell 中的所有变量:

set

这些系统变量为我们获取系统信息提供了方便。

3.2 自定义变量

除了系统预定义变量,我们还可以自己定义变量来存储数据。

定义和使用变量

定义变量的基本格式是:变量名=变量值,注意等号前后不能有空格。使用变量时,在变量名前加 $ 符号。

A=5
echo $A  # 输出 5
重新赋值和撤销变量

可以对已定义的变量重新赋值:

A=8
echo $A  # 输出 8

使用 unset 命令可以撤销变量:

unset A
echo $A  # 变量 A 已被撤销,无输出
只读变量

使用 readonly 命令可以定义只读变量,只读变量不能被重新赋值,也不能被撤销:

readonly B=2
echo $B  # 输出 2
B=9  # 报错:readonly variable
变量的类型

在 bash 中,变量默认都是字符串类型,即使看起来是数字,也不能直接进行数值运算:

C=1+2
echo $C  # 输出:1+2,而不是 3

如果变量的值包含空格,需要使用双引号或单引号将其括起来:

D=I love banzhang   # 错误,会被解析为多个命令
D="I love banzhang" # 正确
echo $D  # 输出:I love banzhang
设置全局变量(供脚本调用)

默认情况下,自定义变量只在当前 Shell 中有效,在子 Shell 或脚本中无法访问。如果要让变量在子 Shell 或脚本中也能使用,需要使用 export 命令将其提升为全局变量:

export B

例如,我们在 helloworld.sh 脚本中添加输出变量 B 的语句:

vim helloworld.sh

内容添加:

#!/bin/bash
echo "helloworld"
echo $B

运行脚本:

./helloworld.sh

如果没有执行 export B 命令,脚本中不会输出变量 B 的值;执行 export B 后重新执行脚本,就可以输出 B 的值了。


3.3 特殊变量

特殊变量是 Shell 中具有特殊含义的变量,用于处理命令行参数、命令执行状态等。

$n$#
  • $n:用于获取命令行参数,$0 代表脚本名称,$1 到 $9 代表第 1 到第 9 个参数,10 及以上的参数需要用 ${10} 表示。
  • $#:用于获取命令行参数的个数。

示例:

vim parameter.sh

内容:

#!/bin/bash
echo '==========$n=========='
echo $0  # 脚本名称
echo $1  # 第一个参数
echo $2  # 第二个参数
echo '==========$#=========='
echo $#  # 参数个数

执行:

chmod 777 parameter.sh
./parameter.sh cls xz

输出:

==========$n==========
./parameter.sh
cls
xz
==========$#==========
2

可以看到,$0 输出了脚本名称 ./parameter.sh$1 输出了第一个参数 cls$2 输出了第二个参数 xz$# 输出了参数的个数 2

$* 和 $@

$* 和 $@ 都用于获取所有命令行参数,但它们在处理参数的方式上有所不同:

  • $*:将所有参数视为一个整体。
  • $@:将每个参数视为独立的个体。

示例:

vim parameter.sh

追加内容:

echo '==========$*=========='
echo $*
echo '==========$@=========='
echo $@

执行:

./parameter.sh a b c d e f g

输出:

==========$*==========
a b c d e f g
==========$@==========
a b c d e f g

从表面上看,$* 和 $@ 的输出结果相同,但在循环中它们的表现不同。我们可以通过一个循环示例来观察:

vim for4.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 for4.sh
./for4.sh cls mly wls

输出:

=============$*=============
ban zhang love cls mly wls
=============$@=============
ban zhang love cls
ban zhang love mly
ban zhang love wls

可以看到,"$*" 被当作一个整体进行循环,只循环一次;而 "$@" 被当作多个独立的参数,循环次数与参数个数相同。

$?

$? 用于获取上一个命令执行的返回状态。如果返回值为 0,表示上一个命令执行成功;如果返回值为非 0,则表示上一个命令执行失败。

例如,判断 helloworld.sh 脚本是否正确执行:

./helloworld.sh
echo $?  # 输出 0,说明执行成功

这在脚本中用于判断命令是否执行成功非常有用。


第 4 章:运算符

在 Shell 中进行数值运算时,不能直接使用 +-*/ 等运算符,需要使用特定的格式。

Shell 中常用的数值运算格式有两种:

  • $((运算式))
  • $[运算式]

例如,计算 (2+3)*4 的值:

S=$[(2+3)*4]
echo $S  # 输出 20

或者:

S=$(( (2+3)*4 ))
echo $S  # 输出 20

这两种格式都可以实现数值运算,使用起来比较方便。


第 5 章:条件判断

在 Shell 中,条件判断用于根据不同的条件执行不同的操作。

5.1 条件判断的基本语法

Shell 中条件判断有两种基本语法:

  1. test condition
  2. [ condition ]  condition 前后要有空格)

如果条件成立,返回 0(表示真);条件不成立,返回非 0(表示假)。需要注意的是,条件非空即为真,[ atguigu ] 返回真,[ ] 返回假。

5.2 常用判断条件及示例

数值比较

用于两个整数之间的比较,常用的运算符有:

  • -eq:等于
  • -ne:不等于
  • -lt:小于
  • -gt:大于
  • -le:小于等于
  • -ge:大于等于

示例:判断 23 是否大于等于 22

[ 23 -ge 22 ]
echo $?  # 输出 0,表示条件成立
文件权限判断

用于判断文件是否具有某种权限,常用的运算符有:

  • -r:有读权限
  • -w:有写权限
  • -x:有执行权限

示例:判断 helloworld.sh 是否具有写权限

[ -w helloworld.sh ]
echo $?  # 输出 0,表示有写权限
文件存在性判断

用于判断文件或目录是否存在,常用的运算符有:

  • -e:文件或目录存在
  • -f:文件存在且是普通文件
  • -d:文件存在且是目录

示例:判断 /home/atguigu/cls.txt 是否存在

[ -e /home/atguigu/cls.txt ]
echo $?  # 如果文件不存在,输出非 0
多条件判断

可以使用 && 和 || 进行多条件判断:

  • &&:表示前一条命令执行成功时,才执行后一条命令
  • ||:表示前一条命令执行失败时,才执行后一条命令

示例:

[ atguigu ] && echo OK || echo notOK  # 输出 OK,因为条件非空为真
[ ] && echo OK || echo notOK          # 输出 notOK,因为条件为空为假

这在脚本中用于复杂的条件判断非常有用。


第 6 章:流程控制

流程控制用于根据不同的条件执行不同的代码块,或重复执行某个代码块。Shell 中的流程控制包括 if 判断、case 语句、for 循环和 while 循环。

6.1 if 判断

if 判断用于根据条件执行不同的代码块,分为单分支和多分支两种形式。

语法格式
  • 单分支:

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

    或者

    if [ 条件判断式 ]
    then
      程序
    fi
    
  • 多分支:

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

注意事项:

  • [ 条件判断式 ] 中,中括号和条件判断式之间必须有空格
  • if 后要有空格
示例

例如,输入数字 1 输出特定内容,输入数字 2 输出另一内容:

vim if.sh

($1为第一个带入的参数)

#!/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
./if.sh 1  # 输出 banzhang zhen shuai


6.2 case 语句

case 语句用于多分支判断,根据变量的不同取值执行不同的代码块。

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

注意事项:

  • case 行尾必须为单词 “in”
  • 每一个模式匹配必须以右括号 “)” 结束
  • 双分号 “;;” 表示命令序列结束,相当于 Java 中的 break
  • 最后的 “*)” 表示默认模式,相当于 Java 中的 default
示例

例如,输入数字 1 输出 "banzhang",输入数字 2 输出 "cls",输入其他数字输出 "renyao":

vim case.sh

#!/bin/bash
case $1 in
"1")
  echo "banzhang"
  ;;
"2")
  echo "cls"
  ;;
*)
  echo "renyao"
  ;;
esac

chmod 777 case.sh
./case.sh 1  # 输出 banzhang


6.3 for 循环

for 循环用于重复执行某个代码块,有两种语法格式。

C 语言风格

语法格式:

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

示例:计算 1 到 100 的和

vim for1.sh

#!/bin/bash
sum=0
for((i=0;i<=100;i++))
do
  sum=$[$sum+$i]
done
echo $sum  # 输出 5050

chmod 777 for1.sh
./for1.sh
列表模式

语法格式:

for 变量 in 值1 值2 ...; do
  程序
done

示例:遍历输出列表中的元素

vim for2.sh

#!/bin/bash
for i in cls mly wls
do
  echo "ban zhang love $i"
done

chmod 777 for2.sh
./for2.sh

输出:

ban zhang love cls
ban zhang love mly
ban zhang love wls
对比 $* 和 $@

前面我们已经介绍了 $* 和 $@ 的区别,这里通过循环进一步展示:

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
./for3.sh cls mly wls

当不使用双引号时,$* 和 $@ 的输出完全一致。

再测试双引号版本

vim for4.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 for4.sh
./for4.sh cls mly wls

输出:

=============$*=============
ban zhang love cls mly wls
=============$@=============
ban zhang love cls
ban zhang love mly
ban zhang love wls

可以更清楚地看到 $* 和 $@ 的区别:"$*" 将所有参数视为一个整体,"$@" 将每个参数视为独立个体。


6.4 while 循环

while 循环用于在条件成立时重复执行某个代码块。

语法格式:

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

示例:计算 1 到 100 的和

vim while.sh

#!/bin/bash
sum=0
i=1
while [ $i -le 100 ]
do
  sum=$[$sum+$i]
  i=$[$i+1]
done
echo $sum  # 输出 5050

chmod 777 while.sh
./while.sh


第 7 章:输入内容到控制台(read 读取)

read 命令用于从控制台读取用户输入,并将输入的值赋给变量。

7.1 read 命令基本语法

read -t 秒数 -p "提示信息" 变量名

选项说明:

  • -p:指定读取输入时的提示信息
  • -t:指定等待输入的时间(单位:秒),如果超过指定时间没有输入,read 命令会退出
  • 默认情况下,read 命令会无限等待用户输入

示例:等待 7 秒输入姓名

文件:read.sh
  1. 创建并编辑脚本:

    touch read.sh
    vim read.sh
    
  2. 输入以下内容:

    #!/bin/bash
    read -t 7 -p "Enter your name in 7 seconds :" NN
    echo $NN
    
执行:
chmod +x read.sh
./read.sh

输出示例:

Enter your name in 7 seconds : atguigu  # 在 7 秒内输入姓名
atguigu  # 输出输入的姓名

(自己输入啊,记得自己输进去啊,别干等着)

如果在 7 秒内没有输入,脚本会继续执行,echo $NN 不会输出任何内容。


第 8 章:函数

函数是一段可以重复调用的代码块,用于实现特定的功能。Shell 中的函数分为系统函数和自定义函数。


8.1 系统函数

系统函数是系统自带的函数,可以直接使用。

8.1.1 basename:提取文件名

basename 命令用于从路径中提取文件名,语法格式:

basename [string / pathname] [suffix]
  • suffix 为后缀,如果指定了 suffixbasename 会将文件名中的 suffix 去掉。

示例:

basename /home/atguigu/banzhang.txt  # 输出 banzhang.txt
basename /home/atguigu/banzhang.txt .txt  # 输出 banzhang

basename 可以理解为取路径里的文件名称。


8.1.2 dirname:提取目录路径

dirname 命令用于从路径中提取目录部分,语法格式:

dirname 文件绝对路径

示例:

dirname /home/atguigu/banzhang.txt  # 输出 /home/atguigu

dirname 可以理解为取文件路径的绝对路径名称。


8.2 自定义函数

除了系统函数,我们还可以自己定义函数来实现特定的功能。

语法格式
function 函数名() {
    语句
    [ return 整数 ]
}

注意事项:

  • Shell 函数必须先定义再调用,因为 Shell 脚本是逐行执行的,不会像其他语言一样先编译
  • 函数的返回值只能通过 $? 系统变量获取,可以使用 return 语句指定返回值,如果不使用 return 语句,函数会以最后一条命令的执行结果作为返回值,返回值的范围是 0 到 255


示例:两个数求和
  1. 创建并编辑脚本:

    touch fun.sh
    vim fun.sh
    
  2. 输入以下内容:

    #!/bin/bash
    
    function sum() {
      s=0
      s=$[$1 + $2]
      echo "$s"
    }
    
    read -p "Please input the number1: " n1
    read -p "Please input the number2: " n2
    sum $n1 $n2
    
  3. 执行脚本:

    chmod +x fun.sh
    ./fun.sh
    
  4. 输出示例:

    Please input the number1: 2
    Please input the number2: 5
    7
    


第 9 章:正则表达式入门

正则表达式是一种用于描述字符串模式的工具,它可以用来匹配、检索和替换符合特定模式的字符串。在 Linux 中,grepsedawk 等文本处理工具都支持正则表达式。

9.1 常规匹配

一串不包含特殊字符的正则表达式会匹配它自己。例如,要在 /etc/passwd 文件中查找包含 atguigu 的行,可以使用:

cat /etc/passwd | grep atguigu

这个命令会匹配所有包含 atguigu 字符串的行。

9.2 常用特殊字符

1)^:行首匹配

^ 用于匹配一行的开头。例如,要在 /etc/passwd 文件中查找以 a 开头的行:

cat /etc/passwd | grep ^a

这个命令会匹配所有以 a 开头的行。

2)$:行尾匹配

$ 用于匹配一行的结尾。例如,要在 /etc/passwd 文件中查找以 t 结尾的行

cat /etc/passwd | grep t$

这个命令会匹配所有以 t 结尾的行🔷1-297🔷。

思考:^$ 匹配什么?
^$ 匹配空行,因为 ^ 表示行首,$ 表示行尾,行首和行尾之间没有任何字符,就是空行。

3).:匹配任意一个字符

. 用于匹配任意一个字符(除了换行符)。例如,要在 /etc/passwd 文件中查找包含 r 开头、t 结尾,中间有两个任意字符的行:

cat /etc/passwd | grep r..t

这个命令会匹配包含 rootrabt 等形式的行。

4)*:上一个字符重复 0 次或多次

* 不能单独使用,它需要和上一个字符连用,表示匹配上一个字符 0 次或多次。例如,要在 /etc/passwd 文件中查找包含 ro*t 的行:

cat /etc/passwd | grep ro*t

这个命令会匹配包含 rtrotrootrooooot 等形式的行。

思考:.* 匹配什么?
.* 表示匹配任意长度的任意字符(除了换行符),因为 . 匹配任意一个字符,* 表示上一个字符(即 .)重复 0 次或多次。

5)[]:字符集合

[] 表示匹配某个范围内的一个字符。例如:

  • [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

这个命令会匹配包含 rtratrbtrabt 等形式的行。

6)\:转义字符

\ 用于转义特殊字符,使其失去特殊含义,仅表示字符本身。由于很多特殊字符在 Shell 中有特定的含义,当我们想匹配这些特殊字符本身时,需要使用 \ 进行转义。例如,要匹配包含 a$b 的行:

cat /etc/passwd | grep 'a\$b'

注意需要使用单引号将正则表达式引起来,防止 $ 被 Shell 解释为变量引用。


第 10 章:文本处理工具

文本处理工具用于对文本文件进行切割、分析、提取等操作,在 Shell 脚本中经常用到。


10.1 cut 命令

cut 命令的作用是从文件的每一行中切割出指定的字节、字符或字段,并将其输出。

1)基本语法:
cut [选项] 文件名

cut 命令的默认分隔符是制表符(\t)。

2)选项说明:
选项 功能
-f 用于指定要提取的列号,例如 -f 1 表示提取第 1 列
-d 用于指定分隔符,默认是制表符,例如 -d " " 表示使用空格作为分隔符
-c 按字符进行切割,后面跟数字表示取第几个字符,例如 -c 1 表示取第 1 个字符

示例 1:准备数据

创建 cut.txt 文件并输入以下内容:

touch cut.txt
vim cut.txt

内容:

dong shen
guan zhen
wo wo
lai lai
le le
示例 2:提取第一列

使用空格作为分隔符,提取第 1 列:

cut -d " " -f 1 cut.txt

输出:

dong
guan
wo
lai
le
示例 3:提取第 2、3 列

使用空格作为分隔符,提取第 2 列和第 3 列:

cut -d " " -f 2,3 cut.txt

输出:

shen
zhen
wo
lai
le
示例 4:查找 guan 并切出

先使用 grep 命令查找包含 guan 的行,再使用 cut 命令提取第 1 列:

cat cut.txt | grep guan | cut -d " " -f 1

输出:

guan
示例 5:PATH 从第 3 个目录起截取

PATH 环境变量存储了系统的命令搜索路径,各路径之间用冒号(:)分隔。要从第 3 个路径开始截取:

echo $PATH | cut -d ":" -f 3-

-f 3- 表示从第 3 列开始提取,直到最后一列。

示例 6:提取 IP 地址

可以结合 ifconfiggrep 和 cut 命令提取 IP 地址:

ifconfig ens33 | grep netmask | cut -d " " -f 10

这个命令的作用是:先通过 ifconfig ens33 获取网卡 ens33 的信息,然后使用 grep netmask 过滤出包含 netmask 的行(通常这一行包含 IP 地址),最后使用 cut 命令以空格为分隔符提取第 10 列,即 IP 地址。


10.2 awk 命令

awk 是一个强大的文本分析工具,它将文件逐行读入,以空格为默认分隔符将每行切割成多个字段,然后对这些字段进行分析处理。

1)基本语法:
awk -F 分隔符 '/模式/{动作}' 文件
  • 模式(pattern):表示 awk 要在数据中查找的内容
  • 动作(action):表示当找到匹配的模式时要执行的命令
2)选项说明:
选项 功能
-F 指定输入文件的分隔符
-v 赋值一个用户定义变量

示例 1:准备 passwd

将 /etc/passwd 文件复制到当前目录,用于测试:

sudo cp /etc/passwd ./

passwd 文件中每一行的格式为:用户名:密码(加密): 用户 ID: 组 ID: 注释:用户家目录:Shell 解析器。

示例 2:匹配 root 并输出第 7 列

使用 : 作为分隔符,查找以 root 开头的行,并输出第 7 列(Shell 解析器):

awk -F : '/^root/{print $7}' passwd

输出:

/bin/bash
示例 3:输出第 1 和第 7 列(用逗号分隔)

使用 : 作为分隔符,查找以 root 开头的行,并输出第 1 列(用户名)和第 7 列(Shell 解析器),中间用逗号分隔:

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

输出:

root,/bin/bash
示例 4:添加列名与尾行

使用 BEGIN 和 END 关键字可以在处理文件内容之前和之后执行指定的动作:

  • BEGIN:在读取任何数据行之前执行
  • END:在处理完所有数据行之后执行

示例:给 passwd 文件的第 1 列和第 7 列添加列名,并在最后添加一行自定义内容:

awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd

输出中,BEGIN 块输出的内容会在文件内容之前显示,END 块输出的内容会在文件内容之后显示。

示例 5:将 UID +1 输出

使用 -v 选项定义变量 i=1,然后将 passwd 文件中第 3 列(用户 ID)的值加 1 并输出:

awk -v i=1 -F : '{print $3+i}' passwd
示例 6:使用内置变量

awk 有一些内置变量,用于获取文件相关的信息:

变量 说明
FILENAME 当前处理的文件名
NR 已读取的记录数(行号)
NF 当前记录的字段个数(切割后列的个数)

示例:统计 passwd 文件的文件名、每行的行号和每行的列数:

awk -F : '{print "filename:" FILENAME ",linenum:" NR ",col:" NF}' passwd
示例 7:查找空行行号

查找 ifconfig 命令输出结果中的空行,并打印空行的行号:

ifconfig | awk '/^$/{print NR}'

/^$/ 是一个正则表达式,表示匹配空行,NR 变量表示当前行的行号。

示例 8:提取 IP 地址

结合 ifconfig 命令和 awk 提取 IP 地址:

ifconfig ens33 | awk '/netmask/ {print $2}'

这个命令的作用是:先获取网卡 ens33 的信息,然后过滤出包含 netmask 的行,最后输出该行的第 2 个字段,即 IP 地址。


第 11 章:综合应用案例

综合应用案例将前面所学的知识结合起来,实现实际的功能。


11.1 归档脚本

该脚本的功能是每天对指定的目录进行归档备份,将目录下的所有文件按天归档保存,并将归档文件放在 /root/archive 目录下,归档文件的名称格式为 archive_目录名_日期.tar.gz

使用 tar 命令进行归档,-c 选项表示归档,-z 选项表示同时进行压缩,得到的文件后缀名为 .tar.gz

脚本内容如下:

#!/bin/bash

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

# 从参数中获取目录名称
if [ -d $1 ]
then
  echo
else
  echo
  echo "目录不存在!"
  echo
  exit
fi

DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)

# 获取当前日期
DATE=$(date +%y%m%d)

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

# 定义归档文件的保存路径
DEST=/root/archive/$FILE

# 开始归档目录文件
echo "开始归档..."
echo

tar -czf $DEST $DIR_PATH/$DIR_NAME

if [ $? -eq 0 ]
then
  echo
  echo "归档成功!"
  echo "归档文件为:$DEST"
  echo
else
  echo "归档出现问题!"
  echo
fi

exit

脚本说明:

  1. 首先判断输入参数的个数是否为 1,如果不是,提示错误并退出
  2. 判断输入的参数是否是一个存在的目录,如果不是,提示错误并退出
  3. 使用 basename 命令获取目录的名称,使用 cd 和 pwd 命令获取目录的绝对路径
  4. 使用 date 命令获取当前日期,格式为 yyyymmdd
  5. 定义归档文件的名称和保存路径
  6. 使用 tar 命令进行归档压缩
  7. 根据 tar 命令的执行结果(通过 $? 判断),提示归档成功或失败


11.2 向用户发送消息

该脚本的功能是向某个用户快速发送消息,需要检测用户是否登录在系统中、是否打开了消息功能,以及当前发送的消息是否为空。

利用 Linux 自带的 mesg 和 write 工具来发送消息。

脚本内容如下:

#!/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

脚本说明:

  1. 使用 who 命令结合 grep 和 awk 检查指定的用户是否登录,如果用户不在线,提示错误并退出
  2. 使用 who -T 命令检查用户是否打开了消息功能(+ 表示打开,- 表示关闭),如果没有打开,提示错误并退出
  3. 检查消息内容是否为空,如果为空,提示错误并退出
  4. 提取消息内容(去掉第一个参数,即用户名)和用户登录的终端
  5. 使用 write 命令向用户发送消息
  6. 根据 write 命令的执行结果,提示发送成功或失败


✅ 总结与建议

通过本文的学习,你已经掌握了 Shell 的基本语法和常用工具的使用,包括:

分类 技能点
脚本基础 shebang、执行方式、权限
变量 系统变量、自定义、只读
控制结构 if、case、for、while
输入输出 read、echo
运算与判断 算术、文件、逻辑
正则表达式 ^ $ . * [] \
文本处理 cut、awk
函数 自定义函数与系统函数
实战案例 归档、消息发送


网站公告

今日签到

点亮在社区的每一天
去签到