首页 > 技术文章 > Shell编程中变量用法

itgiser 2021-03-11 14:29 原文

1. 变量替换

语法 说明
${变量名#匹配规则} 从变量开头进行规则匹配,将符合最短的数据删除
${变量名##匹配规则} 从变量开头进行规则匹配,将符合最长的数据删除,贪婪匹配
${变量名%匹配规则} 从变量尾部进行规则匹配,将符合最短的数据删除
${变量名%%匹配规则} 从变量尾部进行规则匹配,将符合最长的数据删除,贪婪匹配
${变量名/旧字符串/新字符串} 将变量中的第一次出现的旧字符串替换成新字符串 【只替换第一次出现的旧字符串】
${变量名//旧字符串/新字符串} 将变量中的所有的旧字符串替换成新字符串 【全部的旧字符串进行替换】

1.1 示例

$ variable_1="I love you,Do you love me"
$ echo $variable_1
I love you,Do you love me
$ var1=${variable_1#*ov}
$ echo $var1
e you,Do you love me
$ var2=${variable_1##*ov}
$ echo $var2
e me
# 从尾部进行查找,找到第一个即退出
$ var3=${variable_1%ov*}
$ echo $var3
I love you,Do you l
# 从尾部进行查找,贪婪模式,从尾部进行查找,找到最后一个
$ var4=${variable_1%%ov*}
$echo $var4
I l
# 变量替换
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# 只是替换第一个出现的地方
$ var5=${PATH/bin/BIN}
$ echo $var5
/usr/local/BIN:/usr/bin:/bin:/usr/sbin:/sbin
# 全部替换
$ var6=${PATH//bin/BIN}
$ echo $var6
/usr/local/BIN:/usr/BIN:/BIN:/usr/sBIN:/sBIN

2. 字符串处理

2.1 计算字符串长度

语法 说明
方法一 ${#string} 字符串前面加上#,计算字符串的长度
方法二 expr length "$string" 如果字符串string 有空格,则必须加上双引号
# 示例 字符串长度
$ var1="Hello World"
$ len1=${#var1}
$ len2=`expr length "${var1}" `

2.2 获取子串在字符串中的索引位置

语法 说明
方法 expr index $string $substring 并不是获取子串的位置,实际获取的是字符的位置,位置从1开始
# 示例 子串在字符串中的索引位置,实际是字符的位置
$ var1="Hello World"
$ idx=`expr index "${var1}" orld` # 这个获取到的位置,实际上是第一次出现o的位置,返回5

2.3 计算子串长度

语法 说明
方法 expr match $string $substring 可以指定子串,子串必须从字符串的起始位置开始匹配
# 示例 子串长度
$ var1="Hello World"
$ sub_len=`expr match "$var1" World` # 此时返回的是 0,子串必须从头开始匹配
$ sub_len=`expr match "$var1" Hell` # 此时返回的是 4 
$ sub_len=`expr match "$var1" Hell.*` # 此时返回的是 11 匹配的是整个字符串 . 任意字符 * 多个

2.4 抽取子串

语法 说明
方法一 ${string:position} 从变量string中的position位置开始提取 ${string:0}提取所有的字符串
方法二 ${string:position:length} 从position位置开始,匹配长度为 length
方法三 ${string: -position} 冒号(:)后面必须有个空格,负号表示从尾部开始抽取 -1表示最后一位
方法四 ${string:(position)} 如果position为正左边开始,为负从右边开始 同方法三
方法五 expr substr $string $position $length 从position位置开始,匹配长度为 length
$ var1="Hello World"
$ sub1=${var1:2}
$ echo $sub1
llo World
$ sub2=${var1:1}
$ echo $sub2
ello World
$ sub2=${var1:0}
$ echo $sub2
Hello World
$ sub2=${var1: 0}
$ echo $sub2 
Hello World
$ sub2=${var1:(0)}
$ echo $sub2
Hello World
$ sub2=${var1:(-1)}
$ echo $sub2
d
$ sub2=${var1: -1}
$ echo $sub2
d
$ sub2=${var1:0:5}
$ echo $sub2
Hello

2.5 字符串分割

cut基本语法:

cut OPTION... [FILE]...

选项:

  • -f : 通过指定哪一个字段进行提取。cut命令使用“TAB”作为默认的字段分隔符。
  • -d : “TAB”是默认的分隔符,使用此选项可以更改为其他的分隔符。
  • --complement : 此选项用于排除所指定的字段。
  • --output-delimiter : 更改输出内容的分隔符。

下面的代码仅使用分隔符:打印/etc/passwd文件中每一行的第一个字段。

$ cut -d ':' -f 1 /etc/passwd
root
bin
daemon
…
# 下面的代码从/etc/passwd文件提取第一和第六个字段:
$ grep '/bin/bash' /etc/passwd| cut -d ':' -f 1,6
root:/root
bob:/home/bob
user01:/home/user01
# 要显示字段的范围,请指定以-分隔的开始字段和结束字段,如下所示:
$ grep '/bin/bash' /etc/passwd|cut -d ':' -f 1-4,6,7
root:x:0:0:/root:/bin/bash
bob:x:1000:1001:/home/bob:/bin/bash
user01:x:1001:1002:/home/user01:/bin/bash
# 排除所指定的字段
# 在下面的代码中,打印所有字段,除了/etc/passwd文件中的第二个字段:
$ grep '/bin/bash' /etc/passwd|cut -d ':' --complement -f 2
root:0:0:root:/root:/bin/bash
bob:1000:1001::/home/bob:/bin/bash
user01:1001:1002::/home/user01:/bin/bash
# **如何指定一个输出内容的分隔符**
#要指定输出分隔符,请使用--output-delimiter选项。输入分隔符由-d选项指定,默认情况下#输出分隔符与输入分隔符相同。
#先看一下没有使用--output-delimiter选项,是什么样子的:
$ cut -d ':' -f1,7 /etc/passwd|sort
adm:/sbin/nologin
avahi:/sbin/nologin
bin:/sbin/nologin
bob:/bin/bash
# 现在使用--output-delimiter选项,输出分隔符使用’ ‘空格分隔,看一下是什么样子的:
$ cut -d ':' -f1,7 --output-delimiter=' ' /etc/passwd|sort
adm /sbin/nologin
avahi /sbin/nologin
bin /sbin/nologin
bob /bin/bash

# cut命令的一个限制是它不支持指定多个字符作为分隔符。多个空格被视为多个字段分隔符,必须使用tr命令才能得到所需的输出
# Demo 示例
# 需求1. docker images 删除 包含 goharbor 所有的镜像
$ docker images
REPOSITORY                             TAG                         IMAGE ID       CREATED         SIZE
goharbor/redis-photon                  v2.1.3                      161d45dcb2f5   2 months ago    68.9MB
goharbor/harbor-registryctl            v2.1.3                      6c8f14771edd   2 months ago    132MB
goharbor/registry-photon               v2.1.3                      51523bdfa59e   2 months ago    80.3MB
goharbor/nginx-photon                  v2.1.3                      8f3f1df526c4   2 months ago    40.2MB
goharbor/harbor-log                    v2.1.3                      d6143bc93728   2 months ago    106MB
goharbor/harbor-jobservice             v2.1.3                      4940590a5dfb   2 months ago    169MB
goharbor/harbor-core                   v2.1.3                      b9117e275f20   2 months ago    150MB
goharbor/harbor-portal                 v2.1.3                      b76be38158a6   2 months ago    49.6MB
goharbor/harbor-db                     v2.1.3                      7a8a8f4ca96d   2 months ago    172MB
adoptopenjdk/openjdk8-openj9           alpine-slim                 440045c41637   2 months ago    142MB
# 需要获取到第三列 的IMAGE ID,执行 docker rmi 进行删除
# tr -s 替换重复的字符
#  tr '\t' ' ' 转换制表符为空格
$ docker images | grep goharbor  | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f3
161d45dcb2f5
6c8f14771edd
51523bdfa59e
8f3f1df526c4
d6143bc93728
4940590a5dfb
b9117e275f20
b76be38158a6
7a8a8f4ca96d
# 删除镜像
$ docker rmi $(docker images | grep goharbor  | tr '\t' ' ' | tr -s ' ' | cut -d' ' -f3)

# 需求2 获取全路径下的文件名,例如以下两个路径,以 / 分割后,数组的长度也不一样,数组的最后一个为文件名
# /usr/local/gradle-6.8.3/lib/groovy-all-1.3-2.5.12.jar
# /usr/local/gradle-6.8.3/README.md
# rev 命令用于反向输出文件内容,即镜像输出,像镜子一样
# 字符串反向后 分割后的第一个位置 就是文件名,但还是反向的,需要再次反向 rev
$ dir_or_file=获取后全路径信息
$ filename=`echo ${dir_or_file} | rev | cut -d'/' -f1 | rev`

3. 命令替换

命令替换有两种方式

  1. 使用 ` 符号包装命令

  2. 使用$(命令方式)

# 方法1 `command`
$ var1=`date "+%Y-%m-%d %H:%M:%S"`
# 方法2 $(command)
$ var1=$(date "+%Y-%m-%d %H:%M:%S")
#示例 获取nginx进程的个数
$ nginx_process_num=$(ps -ef | grep nginx | grep -v grep | wc -l)

4. 有类型变量

使用的命令有 declare 和 typeset

declare 命令和typeset命令两者等价
declare和typeset都是用来定义变量类型的

  • declare 命令参数表
    | 参数 | 含义, 如果要取消声明的变量,把 - 换成 + 即可 |
    | :--- | :-------------------------------------------- |
    | -r | 将变量设为只读, |
    | -i | 将变量设置为整数 |
    | -a | 将变量定义为数组 |
    | -f | 显示此脚本前定义过的所有函数及内容 |
    | -F | 仅显示此脚本前声明的函数名 |
    | -x | 将变量声明为环境变量,小写的x |
# 只读变量
$ declare -r var1="hell0"
$ echo $var1
hell0
$ var1="aaa"
bash: var1: readonly variable
# 整型变量
$ num1=10 # 默认定义的变量为字符串变量
$ number=$num1+20 # 默认不会进行数学计算
10+20
# 需要需要进行数学计算,使用 expr
$ expr $num1 + 10
20
# 使用decalre声明整型变量
$ declare -i num3=50
$ num3=$num1+90
$ echo $num3
100

# 定义数组
$ declare -a array
$ array=("java" "python" "C" "C++") # 数组赋值,注意用空格进行分割,不是逗号
$ echo ${array[0]} # 输出数组第一个位置上的值, 数组下标索引从0开始
java
$ echo ${#array[@]} # 输出数组中元素的个数
4
$ echo ${#array[1]} # 输出数组中索引下标为1位置的元素的长度
6
$ echo ${array[@]:1:3} # 输出数组中下标冲1位置开始到下标为3位置结束的元素
python C C++
$ echo ${array[@]/on/ON} # 将数组中所有的 on 替换成 ON
java pythON C C++
$ array[0]="go" # 将数组中的下标为0的索引位置上的值进行重新赋值
$ echo ${array[@]}
go python C C++
$ unset array[0] # 删除数组下标为0的那个元素
$  echo ${array[@]}
python C C++
$ unset array # 清空数组中的内容

#定义环境变量
$ declare -x num5 # 声明环境变量num5
$ num5=90
$ declare +x num5 # 取消声明的环境变量

5. bash数学运算

5.1 整数的运算

语法, operator 也可以是比较大小的运算符
方法一 expr $number1 operator $ number2
方法二 $(($number1 operator $ number2)) ,尽量采用expr 进行数学运算
## 1.bash 数学运算之expr
# | & < > * >= <= 等运算符需要进行转译, 如果为真 返回 1,否则返回0
# 两个运算符之间必须都要有空格
# $? 这个值代表命令的返回值
expr $num1 \| $num2  # num1 不为空且非0,返回num1; 否则返回num2
expr $num1 \& $num2  # num1 不为空且非0,返回num1; 否则返回0
expr $num1 \< $num2
expr $num1 \<= $num2
expr $num1 \> $num2
expr $num1 \>= $num2
expr $num1 = $num2
expr $num1 != $num2
expr $num1 + $num2
expr $num1 - $num2
expr $num1 \* $num2
expr $num1 / $num2
expr $num1 % $num2

# 判断 num1 是整数
expr $num + 1 &> /dev/null # 只有整数变量才能进行数学运算,浮点数不可以
# 命令运算结果为 0 表示 可以进行数学运算  $? -eq 0

5.2 浮点数运算

bc是bash内建的运算符,支持浮点数运算
内建变量scale可以设置,默认是0, 指定小数位数

$ echo "23+45" | bc   
68
$ echo "23.2+45" | bc 
68.2
$ echo "23.2+45.65" | bc  
68.85
$ echo "23.2+45.657" | bc
68.857
$echo "23.2/5.657" | bc
4
$ echo "scale=4;23.2/5.657" | bc
4.1011

6.特殊变量

shell编程中有一些特殊的变量可以使用。这些变量在脚本中可以作为全局变量来使用。

名称 说明
$0 脚本名称
$1-9 脚本执行时的参数1到参数9
$? 脚本的返回值
$# 脚本执行时,输入的参数的个数
$@ 输入的参数的具体内容(将输入的参数作为一个多个对象,即是所有参数的一个列表
$* 输入的参数的具体内容(将输入的参数作为一个单词)

$@与$*的区别:

  $@与$*都可以使用一个变量来来表示所有的参数内容,但这两个变量之间有一些不同之处。

  $@:将输入的参数作为一个列表对象

  $*:将输入的参数作为一个单词

推荐阅读