Shell 变量基础与进阶知识

Shell 变量基础与进阶知识

文章目录

  • [Shell 变量基础与进阶知识](#Shell 变量基础与进阶知识)
  • [Shell 变量基础知识](#Shell 变量基础知识)
    • [1 什么是 Shell 变量](#1 什么是 Shell 变量)
      • [1.1 什么是变量](#1.1 什么是变量)
      • [1.2 Shell 变量的特性](#1.2 Shell 变量的特性)
      • [1.3 变量类型](#1.3 变量类型)
    • [2 环境变量](#2 环境变量)
      • [2.1 设置环境变量](#2.1 设置环境变量)
        • [1. 设置环境变量](#1. 设置环境变量)
        • [2. 设置登录后提示信息](#2. 设置登录后提示信息)
      • [2.2 显示与取消环境变量](#2.2 显示与取消环境变量)
      • [2.3 环境变量初始化与对应文件的生效顺序](#2.3 环境变量初始化与对应文件的生效顺序)
      • [2.4 环境变量的知识小结](#2.4 环境变量的知识小结)
    • [3 普通变量](#3 普通变量)
      • [3.1 普通变量定义](#3.1 普通变量定义)
      • [3.2 变量名及变量值要求](#3.2 变量名及变量值要求)
      • [3.3 普通变量的定义及输出示例](#3.3 普通变量的定义及输出示例)
      • [3.4 把命令的结果作为变量的内容赋值](#3.4 把命令的结果作为变量的内容赋值)
    • [4 变量定义技巧总结](#4 变量定义技巧总结)
  • [Shell 变量进阶知识](#Shell 变量进阶知识)
    • [1 Shell 中特殊变量](#1 Shell 中特殊变量)
      • [1.1 Shell 位置参数变量](#1.1 Shell 位置参数变量)
      • [1.2 Shell 进程中的特殊状态变量](#1.2 Shell 进程中的特殊状态变量)
    • [2 Shell 内置变量命令](#2 Shell 内置变量命令)
      • [2.1 echo](#2.1 echo)
      • [2.2 eval](#2.2 eval)
      • [2.3 read](#2.3 read)
      • [2.4 exec](#2.4 exec)
      • [2.5 shift](#2.5 shift)
    • [3 Shell 变量子串知识](#3 Shell 变量子串知识)
      • [3.1 Shell 变量子串介绍](#3.1 Shell 变量子串介绍)
      • [3.2 Shell 变量子串的实践](#3.2 Shell 变量子串的实践)
      • [3.3 变量子串的生产场景应用案例](#3.3 变量子串的生产场景应用案例)
    • [4 Shell 特殊扩展变量](#4 Shell 特殊扩展变量)
      • [4.1 Shell 特殊扩展变量介绍](#4.1 Shell 特殊扩展变量介绍)
      • [4.2 Shell 特殊扩展变量的实践](#4.2 Shell 特殊扩展变量的实践)
      • [4.3 Shell 特殊扩展变量的生产场景应用案例](#4.3 Shell 特殊扩展变量的生产场景应用案例)

Shell 变量基础知识

1 什么是 Shell 变量

1.1 什么是变量

简单地说,变量名是用一个固定的字符串(字符、数字和下划线的组合,不能以数字开头)代替更多、更复杂的内容,该内容里可能还会包含变量、路径、字符串等其他的内容。

变量 是暂时存储数据的地方及数据标记,所存储的数据存在于内存空间中,通过正确地调用内存空间中变量的名字就可以取出与变量对应的数据。使用变量的最大好处就是使程序开发更为方便,当然,在编程中使用变量也是必须的,否则就很难完成相关的程序开发工作。

下面是定义变量和打印变量的示例:

bash 复制代码
# 定义变量,名字为username,对应的内容Tom
[root@server ~]$ username="Tom"

# 打印变量的值
[root@server ~]$ echo $username
Tom

变量的赋值方式为:先写变量名称,紧接着是``字符,最后是值,中间无任何空格。

通过 echo 命令加上 $username 即可输出 username 变量的值。变量的内容一般要加双引号,以防止出错,特别是当值里的内容之间有空格时。

1.2 Shell 变量的特性

默认情况下,在bash Shell 中是不会区分变量类型的 。例如:常见的变量类型为整数、字符串、小数等都当做字符串变量。这和其他强类型语言(例如:Java/C语言)是有区别的,当然,如果需要指定 Shell 变量的类型,也可以使用 declare 命令定义变量的类型,但在一般情况下没有这个需求。

1.3 变量类型

变量根据范围可分为两类:

  • 全局变量 ,在创建它们的 Shell 及其派生出来的任意子进程 Shell 中使用

  • 局部变量,只能在创建它们的 Shell 函数或 Shell 脚本中使用。

变量根据是否是用户自定义也可分为两类:

  • 普通变量:也称为常规变量,由开发者在开发脚本程序时创建。
  • 环境变量:定义shell 执行环境。环境变量又可分为自定义环境变量和bash内置的环境变量。

2 环境变量

环境变量一般是指用export内置命令导出的变量,用于定义 Shell 的运行环境,保证 Shell 命令的正确执行。 Shell 通过环境变量来确定登录用户名、命令路径、终端类型、登录目录等,所有的环境变量都是系统全局变量,可用于所有子进程中,这包括编辑器、 Shell 脚本和各类应用。

环境变量可以在命令行中设置和创建,但用户退出命令行时这些变量值就会丢失,因此,如果希望永久保存环境变量,可在用户家目录下的 .bash_profile.bashrc(非用户登录模式特有,例如远程SSH)文件中,或者全局配置 /etc/bashrc (非用户登录模式特有,例如远程SSH)或 /etc/profile 文件中定义。在将环境变量放入上述的文件中后,每次用户登录时这些变量都将被初始化。

按照系统规范,所有环境变量的名字均采用大写形式。在将环境变量应用于用户进程程序之前,都应该用 export 命令导出定义。有一些环境变量,比如HOMEPATHUSER等,在用户登录之前就已经被/bin/login程序设置好了。通常环境变量被定义并保存在用户家目录下的 .bash_profile 文件或全局的配置文件 /etc/profile 中。

部分bash环境变量展示:

  • EDITOR,默认编辑器。
  • HISTFILE,历史文件位置。
  • HISTSIZE,历史命令个数。
  • PS1,命令行提示符。
  • LANG,Shell 环境语言。
  • PATH,命令搜素路径。
  • HOME,当前用户家目录。
  • USER,当前用户。
  • PWD,当前 Shell 路径。
  • IFS,字符串分隔符。

在查看设置的变量时,有3个命令可以显示变量的值:

  • set 命令 ,输出所有的变量,包括全局变量和局部变量。
  • env 命令 ,只显示全局变量,包括shell的环境。
  • declare 命令输出所有的变量、函数、整数和已经导出的变量set -o 命令显示bash Shell 的所有参数配置信息。

2.1 设置环境变量

1. 设置环境变量

如果想要设置环境变量,就要在给变量赋值之后或在设置变量时使用export命令,具体设置见下文的示例。其实,除了 export 命令,带-x选项的declare内置命令也可以完成同样的功能(注意:此处不要在变量名前面加$)。

export 命令和 declare 命令的格式如下:

  • export 变量名=value
  • 变量名=value ; export 变量名
  • declare -x 变量名=value

示例:

bash 复制代码
export NAME=Tom
declare -x NAME=Tom
NAME=Tom;export NAME

下面来看看让环境变量永久生效的常用设置文件。

  • 用户的环境变量配置文件~/.bash_profile~/.bashrc推荐在(~/.bashrc)文件中设置。

  • 全局环境变量的配置文件/etc/profile/etc/bashrc/etc/profile.d/。**推荐在 /etc/bashrc 文件中设置。**若要在登录后初始化或显示加载内容,则把脚本文件放在/etc/profile.d/下即可(无须加执行权限)。

2. 设置登录后提示信息
  1. 第一种是在 /etc/motd 里增加提示的字符串,如下:

    bash 复制代码
    [root@server ~]$ vim /etc/motd
    welcome to Tom linux Shell training.
    
    #登录后效果
    Welcome to Tom linux Shell training.
    Last login: Sat Dec  3 12:38:23 2022 from 10.1.8.1
  2. 第二种是在 /etc/profile.d/ 下面增加如下脚本。

    bash 复制代码
    [root@server ~]$ sudo vim /etc/profile.d/Tom.sh
    echo Welcome to Tom linux Shell training.

补充

  • 登录前提示符由文件 /etc/issue 提供。
  • 登录后时间提示符由ssh服务提供:在 /etc/ssh/sshd_config 文件中配置PrintLastLog yes,默认启用。

2.2 显示与取消环境变量

  1. 通过echoprintf命令打印环境变量。
  2. envset显示默认的环境变量。
  3. unset消除本地变量和环境变量。

2.3 环境变量初始化与对应文件的生效顺序

在登录 Linux 系统并启动一个bash shell 时,默认情况下 bash 会在若干个文件中查找环境变量的设置。这些文件可统称为系统环境文件。bash 检查的环境变量文件的情况取决于系统运行 Shell 的方式。

系统运行 Shell 的方式一般有3种:

  1. 通过系统用户登录后默认运行的 Shell 。
  2. 非登录交互式运行 Shell 。
  3. 执行脚本运行非交互式 Shell 。

当用户登录 Linux 系统时, Shell 会作为登录 Shell 启动。此时的登录 Shell 加载环境变量的顺序如下图所示。

  1. 用户登录系统后首先会加载 /etc/profile 全局环境变量文件,这是Linux系统上默认的 Shell 主环境变量文件。系统上每个用户登录都会加载这个文件。

  2. 当加载完/etc/profile文件后,才会执行/etc/profile.d目录下的脚本文件,这个目录下的脚本文件有很多,例如:系统的字符集设置等。

  3. 之后开始运行 $HOME/.bash_profile。在这个文件中,又会去找$HOME/.bashrc:如果有,则执行;如果没有,则不执行。在HOME/.bashrc文件中又会去找/etc/bashrc(全局环境变量文件),如果有,则执行,如果没有,则不执行。

  4. 如果用户的 Shell 不是登录时启动的,比如手动敲下bash时启动,那么这种非登录 Shell 只会加载$HOME/.bashrc(用户环境变量文件),并会去找/etc/bashrc(全局环境变量文件)。

    因此如果希望在非登录 Shell 下也可读到设置的环境变量等内容,就需要将变量设定等写入$HOME/.bashrc或者/etc/bashrc,而不是$HOME/.bash_profile/etc/profile

2.4 环境变量的知识小结

  1. 变量名通常要大写。
  2. 变量可以在自身的 Shell 及子 Shell 中使用。
  3. 常用 export 来定义环境变量。
  4. 执行 env 默认可以显示所有的环境变量名称及对应的值。
  5. 输出时用 $变量名,取消时用 unset变量名
  6. 书写 crond 定时任务时要注意,脚本要用到的环境变量最好先在所执行的 Shell 脚本中重新定义。
  7. 如果希望环境变量永久生效,则可以将其放在用户环境变量文件或全局环境变量文件里。

3 普通变量

本地变量在用户当前 Shell 生存期的脚本中使用。例如,本地变量Tom的定义为bingbing, 这个值只在用户当前 Shell 生存期中有意义。如果在 Shell 中启动另一个进程或退出,那么变量Tom的值将会无效。

3.1 普通变量定义

为普通变量的定义赋值,一般有以下3 种写法:

  • 变量名=value,赋值时不加引号。
  • 变量名='value',赋值时加单引号。
  • 变量名="value",赋值时加双引号。

3.2 变量名及变量值要求

  • 变量名 :一般是由字母、 数字 、下划线 组成的,只可以以字母或下划线开头 ,例如:TomTom_123Tom_training
  • 变量值:可以用单引号或双引号引起来,也可不加引号,但是这三者的含义是不同的。

3.3 普通变量的定义及输出示例

采用不同的方式对普通变量进行定义,并打印输出。

示例1:

bash 复制代码
[root@server ~]$ a=192.168.1.2
[root@server ~]$ b='192.168.1.2'
[root@server ~]$ c="192.168.1.2"
[root@server ~]$ echo "a=$a"
a=192.168.1.2
[root@server ~]$ echo "b=$b"
b=192.168.1.2
[root@server ~]$ echo "c=${c}"
c=192.168.1.2

可见, 将连续的普通字符串的内容赋值给变量,不管用不用引号,或者不管用什么引号,它的内容是什么,打印变量时就会输出什么。

示例2:

bash 复制代码
[root@server ~]$ a=192.168.1.2-$a
[root@server ~]$ echo "a=$a"
a=192.168.1.2-192.168.1.2

[root@server ~]$ b='192.168.1.2-$a'
[root@server ~]$ echo "b=$b"
b=192.168.1.2-$a

[root@server ~]$ c="192.168.1.2-$a"
[root@server ~]$ echo "c=${c}"
c=192.168.1.2-192.168.1.2-192.168.1.2 
  1. 变量定义的基本技巧总结

    • a=192.168.1.2-$a, 定义a变量的方式是不加任何引号直接定义变量的内容, 当内容为简单连续的数字、字符串、路径名时,可以这样用,例如: a=1,b=Tom等。不加引号时,值里有变量的会被解析后再输出,上述变量定义中因为$a的值被解析为192.168.1.2 ,因此新的a值就是192.168.1.2-192.168.1.2。

    • b='192.168.1.2-$a',定义b 变量的方式是通过单引号定义。

      这种定义方式的特点是:输出变量内容时单引号里是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。 这种方式比较适合于定义显示纯字符串的情况,即不希望解析变量 、命令等的场景,因此, 对于这里的b 的值,定义时看到的是什么就输出什么,即192.168.1.2-$a。

    • c="192.168.1.2-$a",定义c变量的方式是通过双引号定义变量。

      这种定义方式的特点是:输出变量内容时引号里的变量及命令会经过解析后再输出内容,而不是把双引号中的变量名及命令 (命令需要反引起来)原样输出。 这种方式比较适合于字符串中附带有变量及命令且想将其解析后再输出的变量定义。

3.4 把命令的结果作为变量的内容赋值

常见方法有两种,以ls命令为例:

  • 变量名=`command` ,把命令用反引号引起来,不推荐使用这种方法,因为容易和单引号混淆。
  • **变量名= ( c o m m a n d ) ∗ ∗ ,把命令用 ∗ ∗ (command)** ,把命令用** (command)∗∗,把命令用∗∗()**括起来 ,推荐使用这种方法。
bash 复制代码
[root@server ~]$ ls
script.sh
[root@server ~]$ CMD1=`ls`
[root@server ~]$ echo $CMD1
script.sh

[root@server ~]$ CMD2=$(pwd)
[root@server ~]$ echo $CMD2
/home/Tom

[root@server ~]$ date +%A
Friday
[root@server ~]$ echo Today is $(date +%A).
Today is Friday.

生产场景中把命令的结果作为变量的内容进行赋值的方法在脚本开发时很常见。

4 变量定义技巧总结

可以多学习和模仿操作系统自带的 /etc/init.d/functions 函数库脚本的定义思路,多学习Linux系统脚本中的定义,有经验的读者最终应形成一套适合自己的规范和习惯。

  1. 变量名及变量内容定义小结。

    • 变量名只能包含字母、数字或下划线,只能以字母或下划线开头。

    • 变量名的定义要有一定的规范,并且要见名知意。

      示例:

      bash 复制代码
      # 每个单词的首字母大写的写法
      TomAge=l
      
      # 单词之间用_
      Tom_age=l
      
      # 驼峰语法:首个单词的首字母小写,其余单词首字母大写
      TomAge=l8
      TomSex=man
      
      # 单词全大写
      TomAGE=1
    • 一般的变量定义、赋值常用双引号;简单连续的字符串可以不加引号;希望原样输出时使用单引号。

    • 希望变量的内容是命令的解析结果时,要用反引号'`',或者用$()把命令括起来再赋值。

  2. Shell 定义变量

    a=1 里等号是赋值的意思;比较变量是否相等时也可以用===

  3. 打印输出及使用变量

    • 打印输出或使用变量时,变量名前要接 $ 符号;变量名后面紧接其他字符的时候,要用大括号将变量部分单独括起来;在unset、export、(())等场景中使用但不打印变量时不加$,这个有些例外。
    • 打印输出或使用变量时,一般加双引号或不加引号;如果是字符串变量,最好加双引号;希望原样输出时使用单引号。

Shell 变量进阶知识

文章目录

  • [Shell 变量基础与进阶知识](#Shell 变量基础与进阶知识)
  • [Shell 变量基础知识](#Shell 变量基础知识)
    • [1 什么是 Shell 变量](#1 什么是 Shell 变量)
      • [1.1 什么是变量](#1.1 什么是变量)
      • [1.2 Shell 变量的特性](#1.2 Shell 变量的特性)
      • [1.3 变量类型](#1.3 变量类型)
    • [2 环境变量](#2 环境变量)
      • [2.1 设置环境变量](#2.1 设置环境变量)
        • [1. 设置环境变量](#1. 设置环境变量)
        • [2. 设置登录后提示信息](#2. 设置登录后提示信息)
      • [2.2 显示与取消环境变量](#2.2 显示与取消环境变量)
      • [2.3 环境变量初始化与对应文件的生效顺序](#2.3 环境变量初始化与对应文件的生效顺序)
      • [2.4 环境变量的知识小结](#2.4 环境变量的知识小结)
    • [3 普通变量](#3 普通变量)
      • [3.1 普通变量定义](#3.1 普通变量定义)
      • [3.2 变量名及变量值要求](#3.2 变量名及变量值要求)
      • [3.3 普通变量的定义及输出示例](#3.3 普通变量的定义及输出示例)
      • [3.4 把命令的结果作为变量的内容赋值](#3.4 把命令的结果作为变量的内容赋值)
    • [4 变量定义技巧总结](#4 变量定义技巧总结)
  • [Shell 变量进阶知识](#Shell 变量进阶知识)
    • [1 Shell 中特殊变量](#1 Shell 中特殊变量)
      • [1.1 Shell 位置参数变量](#1.1 Shell 位置参数变量)
      • [1.2 Shell 进程中的特殊状态变量](#1.2 Shell 进程中的特殊状态变量)
    • [2 Shell 内置变量命令](#2 Shell 内置变量命令)
      • [2.1 echo](#2.1 echo)
      • [2.2 eval](#2.2 eval)
      • [2.3 read](#2.3 read)
      • [2.4 exec](#2.4 exec)
      • [2.5 shift](#2.5 shift)
    • [3 Shell 变量子串知识](#3 Shell 变量子串知识)
      • [3.1 Shell 变量子串介绍](#3.1 Shell 变量子串介绍)
      • [3.2 Shell 变量子串的实践](#3.2 Shell 变量子串的实践)
      • [3.3 变量子串的生产场景应用案例](#3.3 变量子串的生产场景应用案例)
    • [4 Shell 特殊扩展变量](#4 Shell 特殊扩展变量)
      • [4.1 Shell 特殊扩展变量介绍](#4.1 Shell 特殊扩展变量介绍)
      • [4.2 Shell 特殊扩展变量的实践](#4.2 Shell 特殊扩展变量的实践)
      • [4.3 Shell 特殊扩展变量的生产场景应用案例](#4.3 Shell 特殊扩展变量的生产场景应用案例)

1 Shell 中特殊变量

1.1 Shell 位置参数变量

在 Shell 中存在一些特殊且重要的变量,例如:$0$1,我们称之为位置参数变量。要从命令行、函数或脚本执行等处传递参数时,就需要在 Shell 脚本中使用位置参数变量。

部分位置参数变量如下:

  1. $0 ,获取当前执行的 Shell 脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径。
  2. ** n ∗ ∗ ,获取当前执行的 S h e l l 脚本的 ∗ ∗ 第 n 个参数值 ∗ ∗ 。如果 n 大于 9 ,则用大括号括起来,例如 n**,获取当前执行的 Shell 脚本的**第n个参数值**。如果n大于9,则用大括号括起来,例如 n∗∗,获取当前执行的Shell脚本的∗∗第n个参数值∗∗。如果n大于9,则用大括号括起来,例如{10},接的参数以空格隔开。
  3. $# ,获取当前执行的 Shell 脚本后面接的参数数量
  4. ** ∗ ∗ ∗ ,获取当前 S h e l l 脚本所有传参的参数,不加引号和 ' ***,获取当前 Shell 脚本所有传参的参数,不加引号和` ∗∗∗,获取当前Shell脚本所有传参的参数,不加引号和'@相同;如果给 ∗ ' 加上双引号,例如: ' " *`加上双引号,例如:`" ∗'加上双引号,例如:'"*",则表示将所有的参数视为单个字符串,相当于1 2 $3`。
  5. ** @ ∗ ∗ ,获取当前 S h e l l 脚本所有传参的参数,不加引号和 @**,获取当前 Shell 脚本所有传参的参数,不加引号和 @∗∗,获取当前Shell脚本所有传参的参数,不加引号和*相同;如果给 @ 加上双引号,例如: ' @加上双引号,例如:` @加上双引号,例如:'@,则表示将所有的参数视为不同的独立字符串,相当于"1"、"2"、"$3"...`,这是将多参数传递给其他程序的最佳方式,因为它会保留所有的内嵌在每个参数里的任何空白。

示例1showargs.sh 内容如下

bash 复制代码
#!/bin/bash
echo $0
echo $1
echo $2
echo $10
echo ${10}
echo $#
echo $*
echo $@
echo "$@"
bash 复制代码
[root@server ~]$ bash showargs.sh {a..z}
showargs.sh
a
b
a0
j
26
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z

示例2:ssh_ctl 内容如下

bash 复制代码
#!/bin/bash
systemctl $1 sshd

执行效果如下:

bash 复制代码
[root@server ~]$ sudo ssh_ctl stop
[root@server ~]$ sudo ssh_ctl status
[root@server ~]$ sudo ssh_ctl start

1.2 Shell 进程中的特殊状态变量

$?

作用:获取执行上一个指令的执行状态返回值:0为成功,非零为失败,这个变量最常用。

bash 复制代码
[root@server ~]$ ls
hello  script.sh  showargs.sh
[root@server ~]$ echo $?
0
[root@server ~]$ ls /root
ls: 无法打开目录/root: 权限不够
[root@server ~]$ echo $?
2

# man ls查看,退出码含义
[root@server ~]$ man ls
......
   Exit status:
       0      if OK,
       1      if minor problems (e.g., cannot access subdirectory),
       2      if serious trouble (e.g., cannot access command-line argument).
$$(不重要)

作用:获取当前执行的 Shell 脚本的进程号(PID),这个变量不常用,了解即可。

bash 复制代码
[root@server ~]$ echo $$
2447
[root@server ~]$ kill -9 $$

Connection closed by foreign host.

Disconnected from remote host(10.1.8.88) at 21:00:36.

Type `help' to learn how to use Xshell prompt.
$!(不重要)

作用:获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可。

bash 复制代码
[root@server ~]$ md5sum /dev/zero &
[1] 2611
[root@server ~]$ echo $!
2611
[root@server ~]$ ps o pid,%cpu,%mem,command $!
   PID %CPU %MEM COMMAND
  2611 99.1  0.0 md5sum /dev/zero
[root@server ~]$ kill $!
[1]+  Terminated              md5sum /dev/zero
$_

作用:获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可。

bash 复制代码
[root@server ~]$ ls /etc/hosts /etc/fstab /etc/hostname
/etc/fstab  /etc/hostname  /etc/hosts
[root@server ~]$ cat $_
Tom-shell
[root@server ~]$ cat /etc/hostname
Tom-shell

2 Shell 内置变量命令

bash Shell 包含一些内置命令。 这些内置命令在目录列表里是看不见的,它们由 Shell 本身提供。常用的内部命令有: echoevalexecreadshift等。

下面简单介绍几个最常用的内置命令的格式和功能。

2.1 echo

echo命令参数选项:

  • -n,不换行输出内容。
  • -e,解析转义字符(见下面的字符)

转义字符:

  • \n,换行。
  • \t,制表符(tab)。
  • \b,退格。
bash 复制代码
[root@server ~]$ echo -n "Tom ";echo Jerry
Tom Jerry
[root@server ~]$ echo -e "Tom\nJerry"
Tom
Jerry
[root@server ~]$ echo -e "Tom\tJerry"
Tom	Jerry

[root@server ~]$ echo -e "1\b23"
23

[root@server ~]$ echo -e "123\b"
123

# 后面有东西才会覆盖
[root@server scripts]$ echo -ne "123\b";echo haha
12haha

2.2 eval

当 Shell 程序执行到 eval 语句时, Shell 读入参数args,并将它们组合成一个新的命令,然后执行。

示例1:

bash 复制代码
[root@server ~]$ vim noeval.sh
echo \$$#
[root@server ~]$ bash noeval.sh hello world
$2

[root@server ~]$ vim eval.sh
eval "echo \$$#"
[root@server ~]$ bash eval.sh hello world
world

示例2:

bash 复制代码
[root@server ~]$ ssh-agent 
SSH_AUTH_SOCK=/tmp/ssh-LmBsuYheypEC/agent.1589; export SSH_AUTH_SOCK;
SSH_AGENT_PID=1590; export SSH_AGENT_PID;
echo Agent pid 1590;

[root@server ~]$ eval $(ssh-agent)
Agent pid 1598
# 等效于
[root@server ~]$ ssh-agent > /tmp/env
[root@server ~]$ source /tmp/env
[root@server ~]$ rm -f /tmp/env

2.3 read

从标准输入读取字符串等信息, 传给 Shell 程序内部定义的变量。

bash 复制代码
[root@server ~]$ cat read.sh 
#!/bin/sh
read -p "输入你想要说的话:" str
echo "你想要说的话是:$str"

[root@server ~]$ bash read.sh 
输入你想要说的话:`hello world`
你想要说的话是:hello world

# 不显示输入的内容
[root@server ~]$ read -s -p "请设置用户密码: " password 
请设置用户密码: 

[root@server ~]$ echo $password
redhat

2.4 exec

exec 命令能够在不创建新的子进程的前提下, 转去执行指定的命令, 当指定的命令执行完毕后, 该进程 (也就是最初的 Shell ) 就终止了。

示例1:

bash 复制代码
[root@server ~]$ sudo -i
[root@shell ~]# ps $$
   PID TTY      STAT   TIME COMMAND
  1741 pts/0    S      0:00 -bash
[root@shell ~]# exec sleep 10
# sleep命令运行完成后自动返回到原先普通用户shell了。
[root@server ~]$ 

# 在exec命令执行期间,新开一个窗口,查看PID是1741的进程信息
[root@server ~]$ ps 1741
   PID TTY      STAT   TIME COMMAND
  1741 pts/0    S+     0:00 sleep 10

示例2: 读取文件内容

bash 复制代码
[root@server ~]$ cat exec.sh 
#!/bin/bash

# 生成序列内容文件
seq 5 > /tmp/seq.log

# 从文件中读取内容,作为shell的标准输入
exec < /tmp/seq.log

# read命令接收标准输入
while read line
do
  echo $line
done

[root@server ~]$ bash exec.sh
1
2
3
4
5

2.5 shift

shift 语句会按如下方式重新命名所有的位置参数变量,即2成为1、$3成为 2 等,以此类推,在程序中每使用一次 s h i f t 语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数 2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数 2等,以此类推,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数#减1,直到减到0为止。

如果脚本有3个参数,那么执行一次shift后 ,3就变成了2,2变成了1,原先的$1就消失了。

示例1:

bash 复制代码
[root@server ~]$ cat shift.sh 
#!/bin/sh
echo $1
shift
echo $1

[root@server ~]$ bash shift.sh hello world
hello
world

示例2: shell脚本接收中横杠-开头的选项参数。

bash 复制代码
[root@server ~]$ cat shift.sh 
#!/bin/sh
if [ "$1" = "-c" ];then
  shift
fi
command="$1"
echo $command
[root@server ~]$ bash shift.sh -c date
date

3 Shell 变量子串知识

3.1 Shell 变量子串介绍

读者可以执行 man bash 命令,然后搜索"Parameter Expansion"查找相关的帮助内容。

==注意:==对于 Shell 新手来说,此部分内容可以暂时忽略,在学完本书后再回来学习。

常见 Shell 变量子串说明:

  • ${parameter},返回变量 parameter 的内容。
  • ${#parameter},返回变内容的长度(按字符),也适用于特殊变量。
  • ${parameter:offset},在变量parameter中,从位置offset之后开始提取子串到结尾。
  • ${parameter:offset:length} ,在变量parameter中,从位置offset之后开始提取长度为length
    的子串。
  • ${parameter#word},从变量parameter开头开始删除最短匹配的word子串。
  • ${parameter##word},从变量parameter开头开始删除最长匹配的word子串。
  • ${parameter%word},从变量parameter结尾开始删除最短匹配的word子串。
  • ${parameter%%word},从变量parameter结尾开始删除最长匹配的word子串。
  • ${parameter/pattem/string},使用string代替第一个匹配的pattern。
  • ${parameter//pattem/string},使用string代替所有匹配的pattern。

3.2 Shell 变量子串的实践

示例1: ${parameter}

bash 复制代码
[root@server ~]$ str="abc123abc123"

[root@server ~]$ echo ${str}
abc123abc123

示例2: ${#parameter}

bash 复制代码
[root@server ~]$ str="abc123abc123"

# 此方法获取字符串长度速度最快
[root@server ~]$ echo ${#str}
12

# 其他方式计算字符串长度
[root@server ~]$ echo ${str} | wc -L
12
[root@server ~]$ expr length "${str}"
12
[root@server ~]$ echo "$str" | awk '{ print length($0)}'
12

示例3: ${parameter:offset}${parameter:offset:length}

bash 复制代码
[root@server ~]$ str="abc123abc123"

# 提取子串
[root@server ~]$ echo ${str:3}
123abc123
[root@server ~]$ echo ${str:3:4}
123a

示例4: ${parameter#word}${parameter##word}

bash 复制代码
[root@server ~]$ str="abc123abc123"

# 从左侧开始删除子串
[root@server ~]$ echo ${str#b*a}
abc123abc123

# 未匹配到,第一个字符必须与元字串第一个子符一致
[root@server ~]$ echo ${str#a*c}
123abc123
[root@server ~]$ echo ${str##a*c}
123

示例5: ${parameter%word}${parameter%%word}

bash 复制代码
[root@server ~]$ str="abc123abc123"

# 从右侧开始删除子串
[root@server ~]$ echo ${str%a*c}
abc123abc123

# 未匹配到,最后一个字符必须与元字串最后一个子符一致
[root@server ~]$ echo ${str%c*3}
abc123ab
[root@server ~]$ echo ${str%%c*3}
ab

示例6: ${parameter/pattem/string}

bash 复制代码
[root@server ~]$ str="abc123abc123"

# 只替换第一个
[root@server ~]$ echo ${str/abc/def}
def123abc123

# 所有的全替换
[root@server ~]$ echo ${str//abc/def}
def123def123

正则表达式处理变量子串

bash 复制代码
[root@server ~ 14:58:21]# str="abc123abc123"
[root@server ~ 15:06:15]# echo $str
abc123abc123
[root@server ~ 15:06:19]# echo $str | egrep '^a.*1'
abc123abc123
[root@server ~ 15:07:04]# str="abc123abc123abc123"
[root@server ~ 15:07:27]# echo $str | egrep '^a.*1'
abc123abc123abc123
[root@server ~ 15:07:30]# str="abc123abc123"
[root@server ~ 15:07:42]# echo $str | egrep '^a.*1'
abc123abc123
[root@server ~ 15:07:44]# echo $str | egrep '^a[^1]+1'
abc123abc123
[root@server ~ 15:08:23]# str="abc123abc123abc123"
[root@server ~ 15:10:09]# echo $str | egrep '^a[^1]+1'
abc123abc123abc123
[root@server ~ 15:10:11]# echo $str | egrep '^a[^1]+1[^1]+1'
abc123abc123abc123

3.3 变量子串的生产场景应用案例

**示例1:**替换文件名中特定字符串

bash 复制代码
[root@server ~]$ touch stu-202212-snap.jpg
[root@server ~]$ ls stu-*
stu-202212-snap.jpg

[root@server ~]$ file="stu-202212-snap.jpg"
[root@server ~]$ mv $file ${file/2022/2021}
[root@server ~]$ ls stu-*
stu-202112-snap.jpg

**示例2:**删除文件名中特定字符串

bash 复制代码
[root@server ~]$ touch stu-202212-snap.jpg
[root@server ~]$ ls stu-*
stu-202212-snap.jpg

[root@server ~]$ file="stu-202212-snap.jpg"
[root@server ~]$ mv $file ${file/-snap/}
[root@server ~]$ ls stu-*
stu-202212.jpg

4 Shell 特殊扩展变量

4.1 Shell 特殊扩展变量介绍

读者可以执行man bash命令,然后搜索"Parameter Expansion"查找相关的帮助内容。

==注意:==对于 Shell 新手来说,此部分内容可以暂时忽略,在学完本书后再回来学习。

常见 Shell 特殊扩展变量使用说明:

  • ${parameter:-word},如果parameter的变量值为空或未赋值,则会返回word字符串。

    **用途:**如果变量未定义,则返回备用的值,防止变量为空值或因未定义而导致异常。

  • ${parameter:=word},如果parameter的变量值为空或未赋值,则设置这个变量值为word,并返回其值。位置变量和特殊变量不适用。

    **用途:**基本同上一个${parameter:-word},但该变量又额外给parameter变量赋值了。

  • ${parameter:?word} ,如果parameter变量值为空或未赋值,那么word字符串将被作为标准错误输出,否则输出变量的值。

    **用途:**用于捕捉由于变量未定义而导致的错误,并退出程序。

  • ${parameter:+word},如果parameter变量值为空或未赋值,则什么都不做,否则word字符串将替代变量的值。

说明: **每个表达式内的冒号都是可选的。**如果省略了表达式中的冒号,则将每个定义中的"为空或未赋值"部分改为"未赋值",也就是说,运算符仅用于测试变量是否未赋值。

4.2 Shell 特殊扩展变量的实践

示例1: ${parameter:-word}

bash 复制代码
# 变量未定义情况
[root@server ~]$ unset test
[root@server ~]$ echo ${test:-UNSET}
UNSET
[root@server ~]$ echo $test

# 变量定义情况
[root@server ~]$ test=hello
[root@server ~]$ echo ${test:-UNSET}
hello

示例2: ${parameter:=word}

bash 复制代码
# 变量未定义情况
[root@server ~]$ unset SHELL
[root@server ~]$ echo ${SHELL:=/bin/bash}
/bin/bash
[root@server ~]$ echo $SHELL
/bin/bash

# 变量定义情况
[root@server ~]$ echo ${SHELL:=/bin/sh}
/bin/bash
[root@server ~]$ echo $SHELL
/bin/bash

示例3: ${parameter:?word}

bash 复制代码
# 变量未定义情况
[root@server ~]$ unset test
[root@server ~]$ echo ${test:?UNSET}
-bash: test: UNSET

# 变量定义情况
[root@server ~]$ test=hello
[root@server ~]$ echo ${test:?UNSET}
hello

示例4: ${parameter:+word}

bash 复制代码
# 变量未定义情况
[root@server ~]$ unset test
[root@server ~]$ echo ${test:+UNSET}

# 变量定义情况
[root@server ~]$ test=hello
[root@server ~]$ echo ${test:+UNSET}
UNSET

4.3 Shell 特殊扩展变量的生产场景应用案例

**示例1:**实现 Apache 服务启动脚本/etc/init.d/httpd

bash 复制代码
# /bin/bash
......
# Start httpd in the C locale by default.
HTTPD_LANG=${HTTPD_LANG:-"C" }
......
httpd=${HTTPD:-/usr/sbin/httpd}
pidfile=${PIDFILE:-/var/run/httpd.pid}
lockfile=${LOCKFILE:-/var/lock/subsys/httpd}

**示例2:**删除过期文件

bash 复制代码
#!/bin/bash
# 防止path变量未定义,导致异常结果
find ${path:-/tmp} -name "*.tar.gz" -type f -mtime +7 |xargs rm -f

在企业中,针对目录路径等的处理就可以采用上述变量不存在(即赋指定值)的方式,防止因目录路径不存在而导致的异常。

例如:变量如果为NULL或没有定义,则赋予一个备用的值,特别是针对变量的删除操作,这种方式会很有用,否则所删除的变量如果不存在,则可能导致未知的危险。

相关推荐
Web极客码9 小时前
如何通过命令行工具检查 Linux 版本信息
linux·运维·服务器
Lynnxiaowen9 小时前
今天我们继续学习Kubernetes内容pod资源对象
运维·学习·容器·kubernetes·云计算
欢鸽儿9 小时前
Vitis】Linux 下彻底清除启动界面 Recent Workspaces 历史路径
linux·嵌入式硬件·fpga
繁华似锦respect10 小时前
C++ 智能指针底层实现深度解析
linux·开发语言·c++·设计模式·代理模式
远程软件小帮手10 小时前
云电脑挂机功能上线!边上班摸鱼边游戏多开教程
运维·服务器
hweiyu0011 小时前
Linux 命令:dd
linux
---学无止境---11 小时前
i386 架构中断管理函数详解
linux·架构
cike_y11 小时前
浅谈用docker搭建一个ctf镜像
运维·安全·网络安全·docker·容器·ctf
云和数据.ChenGuang11 小时前
运维工程师技术之openEuler 网卡基础查询指令
运维·中间件·mycat·运维工程师·运维技术