Bash 字符串操作
Bash 字符串操作
Bash 中大部分的变量包括文件都可以当作字符串进行操作,因此本章的内容非常实用
字符串的长度
获取字符串长度的语法如下。
${#varname}
下面是一个例子。引号中的字符串也算长度。
$ name="xiashuo.xyz hello world"
$ echo ${#name}
23
大括号 {}
是必需的,否则 Bash 会将 $#
理解成脚本的参数个数,将变量名理解成文本。
$ echo $#myvar
0myvar
上面例子中,Bash 将 $#
和 myvar
分开解释了。
关于
$#
的分析,请看《Bash 脚本》
子字符串
字符串提取子串的语法如下。
${varname:offset:length}
上面语法的含义是返回变量 $varname
的子字符串,
首先我们需要明白一个下标定义,从左往右数,即正序的时候,第一个字符的下标是 0,从右往左数,即倒序的时候,第一个数的下标是 -1。
-
offset
为正数-
length
为正数的时候,length
的意义为字串的长度,表达式的的作用:是返回varname
按正序从位置offset
开始,包含offset
位置的字符,往右数长度为length
,的子串。 -
length
为负数的时候,length
的意义为last_offset
,表示子串中最后一个字符的位置,且不包含,表达式的的作用:是返回varname
按正序从位置offset
开始,包含offset
位置的字符,到按倒叙位置为last_offset
,不包含last_offset
位置的字符,的子串。 -
如果省略
length
,则从位置offset
开始,一直返回到字符串的结尾。
-
-
offset
为负数,如果offset
为负值,表示按倒叙定位。注意,负数前面必须有一个空格,以防止与${variable:-word}
的变量的设置默认值语法混淆。关于${variable:-word}
的语法,请看《Bash 变量》的检查变量的值是否为空
小节。-
length
为正数的时候,length
的意义为字串的长度,表达式的的作用:是返回varname
按倒叙从位置offset
开始,包含offset
位置的字符,往右数长度为长度为length
,的子串。 -
length
为负数的时候,length
的意义为last_offset
,表示子串中最后一个字符的位置,且不包含,表达式的的作用:是返回varname
按倒叙从位置offset
开始,包含offset
位置的字符,到按倒叙位置为last_offset
,不包含last_offset
位置的字符,的子串。 -
如果省略
length
,则从位置offset
开始,一直返回到字符串的结尾。
-
$ echo $name
0123456
$ echo ${name:2:3}
234
$ echo ${name:2:-1}
2345
$ echo ${name:2}
23456
$ echo ${name:9}
$ echo ${name: -4:2}
34
$ echo ${name: -4:-1}
345
$ echo ${name: -4:-5}
-bash: -5: substring expression < 0
$ echo ${name: -4}
3456
$ echo ${name: -9}
$ echo $name
0123456
我们可以通过一个变量来接受子字符串。获取子字符串的操作不会修改原字符串
$ name_sub=${name:3:2}
$ echo $name $name_sub
0123456 34
注意,不能直接对字符串字面量进行获取子字符串的操作,只能对值为字符串的变量执行获取子字符串的操作
$ echo ${"123456":3:2}
-bash: ${"123456":3:2}: bad substitution
字符串拼接
字符串的拼接很简单,直接写在一起即可
双引号字符串的拼接
# 字符串拼接字符串
str_concatnate="111""22"
echo $str_concatnate
# 字符串拼接变量
delimitor="+"
str_concatnate="111"$delimitor
echo $str_concatnate
str_concatnate="111"$delimitor"22"
echo $str_concatnate
输出
11122
111+
111+22
单引号字符串的拼接
# 字符串拼接字符串
str_concatnate='111''22'
echo $str_concatnate
# 字符串拼接变量
delimitor='+'
str_concatnate='111'$delimitor
echo $str_concatnate
str_concatnate='111'$delimitor'22'
echo $str_concatnate
输出
11122
111+
111+22
查找字符所在的位置
# 查找字符 c 或 8 的位置(哪个字母先出现就计算哪个):
# 输出 3
$ name=abcd123465789
$ echo "$(expr index "$name" c8)"
3
《Bash 的算术运算》的
expr 命令
小节
搜索和替换
Bash 提供多种模式的匹配和替换的方法
字符串头部的模式匹配
-
${variable#pattern}
如果 pattern 匹配变量 variable 的开头,删除最短匹配(非贪婪匹配)的部分,返回剩余部分,原字符串不会有变化 -
${variable##pattern}
如果 pattern 匹配变量 variable 的开头,删除最长匹配(贪婪匹配)的部分,返回剩余部分,原字符串不会有变化 -
${variable/#pattern/string}
如果 pattern 匹配变量 variable 的开头,就会将最长匹配(贪婪匹配)的部分替换成 string,原字符串不会有变化
不存在语法
${variable/##pattern/string}
${variable#pattern}
和${variable##pattern}
可以理解为/string
中的string
为空字符串。默认将匹配部分替换成空字符串,也就是删除
匹配模式 pattern
可以使用 *
、?
、[]
等通配符。
如果字符串里面无法匹配模式 pattern
,则对字符串不会发生任何修改,最终返回原字符串。
简单实践一下:
$ str='xiashuo.xyz hello world!!!'
$ echo $str
xiashuo.xyz hello world!!!
$ echo ${str#*.}
xyz hello world!!!
# 最短匹配
$ echo ${str#*o}
.xyz hello world!!!
# 最长匹配
$ echo ${str##*o}
rld!!!
# 按最长匹配替换
$ echo ${str/#*o/111}
111rld!!!
常用用法,比如只获取文件名称(包括拓展名)、仅获取文件拓展类型、仅获取文件名(不包括拓展类型)
# 获取文件名+拓展名
$ echo ${file_path##*/}
Main.java
# 获取拓展名
$ echo ${file_path##*.}
java
# 仅获取文件名
$ file_name_full=${file_path##*/}
$ echo ${file_name_fullpattern}` 如果 pattern 匹配变量 variable 的结尾,删除最长匹配(贪婪匹配)的部分,返回剩余部分,原字符串不会有变化
- `${variable/%pattern/string}` 如果 pattern 匹配变量 variable 的结尾,就会将最长匹配(贪婪匹配)的部分替换成 string,原字符串不会有变化
> 不存在语法 `${variable/pattern}` 可以理解为 `/string` 中的 `string` 为空字符串。默认将匹配部分替换成空字符串,也就是删除
匹配模式 `pattern` 可以使用 `*`、`?`、`[]` 等通配符。
如果字符串里面无法匹配模式 `pattern`,则对字符串不会发生任何修改,最终返回原字符串。
简单实践:
```shell
$ str="xiashuo.xzy call my number\"158-4571-2256\""
$ echo $str
xiashuo.xzy call my number"158-4571-2256"
# 最短匹配
$ echo ${str%-*}
xiashuo.xzy call my number"158-4571
# 最长匹配
$ echo ${str%%-*}
xiashuo.xzy call my number"158
# 匹配引号,需要转义
$ echo ${str%%\"*\"}
xiashuo.xzy call my number
# 替换
$ echo ${str/%\"*\"/secret}
xiashuo.xzy call my numbersecret
常用用法,比如只获取文件名称(包括拓展名)、仅获取文件拓展类型、仅获取文件名(不包括拓展类型)
具体实践请看 字符串头部的模式匹配
小节
任意位置的模式匹配
-
${variable/pattern/string}
如果 pattern 匹配变量 variable 的一部分,最长匹配(贪婪匹配)的那部分被 string 替换,但仅替换第一个匹配 -
${variable//pattern/string}
如果 pattern 匹配变量 variable 的一部分,最长匹配(贪婪匹配)的那部分被 string 替换,所有匹配都替换
上面两种语法都是最长匹配(贪婪匹配)下的替换,区别是前一个语法仅仅替换第一个匹配,后一个语法替换所有匹配。
# 模式可以包含 /
$ echo ${fila_path////-}
-opt-shell-ts.sh
# 为了可读性,可以加上引号
$ echo ${fila_path/'/'/-}
-opt/shell/ts.sh
$ echo ${fila_path//'/'/-}
-opt-shell-ts.sh
# 省略第二个 / 后面的内容或者省略第二个 / 都是默认替换成空字符串,也就是删除
$ echo ${fila_path//'/'/}
optshellts.sh
$ echo ${fila_path//'/'}
optshellts.sh
一个简单的应用是看 PATH 环境变量
将 :
替换成换行,然后可以一行一个路径,看着束缚很多
这个其实给了我们一种将单个字符串拆分成多行的思路,拆分成多行之后,再一行一行地读取到数组中。
$ echo $PATH
KAFKA_HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/lib/jvm/java/bin:/usr/lib/jvm/java/jre/bin:/root/bin
$ echo -e ${PATH//:/'\n'}
KAFKA_HOME/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/usr/lib/jvm/java/bin
/usr/lib/jvm/java/jre/bin
/root/bin
将单行字符串变成数组
关于数组的知识,请看《Bash 数组》
$ arr_str="111,2222,333"
$ arr=(${arr_str//,/' '})
$ echo ${#arr[@]}
3
$ echo ${arr[@]}
111 2222 333
此外还有两种特殊形式,我们在 字符串头部的模式匹配
和 字符串尾部的模式匹配
中实际上就已经学习过了
# 模式必须出现在字符串的开头
${variable/#pattern/string}
# 模式必须出现在字符串的结尾
${variable/%pattern/string}
改变大小写
-
${varname^^}
转为大写 -
${varname,,}
转为小写
简单实践
$ tese_var=abcdefg
$ echo ${tese_var^^}
ABCDEFG
$ tese_var=ABCDEFG
$ echo ${tese_var,,}
abcdefg