BashShell字符串

1. 简介

BashShell 字符串内置许多字符串操作,包括有字符串切割、替换、分割等。

2. ${} 语法

2.1 字符串截取

举例来说:对于字符串 file=/dir1/dir2/dir3/my.file.txt,可以用 ${} 分别替换得到不同的值:

  • ${file#*/}: 删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
  • ${file##*/}: 删掉最后一个 / 及其左边的字符串:my.file.txt
  • ${file#*.}: 删掉第一个 . 及其左边的字符串:file.txt
  • ${file##*.}: 删掉最后一个 . 及其左边的字符串:txt
  • ${file%/*}: 删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3
  • ${file%%/*}: 删掉第一个 / 及其右边的字符串:(空值)
  • ${file%.*}: 删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
  • ${file%%.*}: 删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my

记忆方法:

1
2
3
# 是去掉左边(键盘上 # 在 $ 的左边)
% 是去掉右边(键盘上 % 在 $ 的右边)
单一符号是最小匹配;两个符号是最大匹配

2.2 子串提取、替换

对于字符串 file=/dir1/dir2/dir3/my.file.txt,还可以使用 ${} 语法来进行子串提取和替换:

  • ${file: start: length} 语法:
  • ${file: 0: 5}:提取最左边的 5 个字节:/dir1
  • ${file: 5: 5}:提取从左往右数第 5 个字节右边的连续 5 个字节:/dir2
  • ${file: 5}:提取第 5 个字节右边的所有字节:/dir2/dir3/my.file.txt
  • ${file: -8: 4}:提取从右往左数第 8 个字节右边的连续 4 个字节:file
  • ${file: -8}:提取从右往左数第 8 个字节右边的所有字节:file.txt
  • ${file/dir/path}:将第一个 dir 提换为 path/path1/dir2/dir3/my.file.txt
  • ${file//dir/path}:将全部 dir 提换为 path/path1/path2/path3/my.file.txt
  • ${#file}:获取字符串长度:27

2.3 字符串 \Leftrightarrow 数组

${} 语法默认使用 IFS 指定的分隔符分隔字符串(即空白符:空格、tab 符、换行符),默认使用空格连接字符串元素。

  • ${str[*]}:得到数组所有元素作为一个长字符串整体(all of the elements)

  • ${str[@]}:得到数组每一个元素,即子串数组(each of elements)

  • ${#str[*]}:得到数组元素的个数(number of elements)

  • ${!str[*]}:得到数组所有元素的下标(all indexs of elements)

  • ${!str[@]}:得到数组每一个元素的下标(each index of elements)

【注】此数组非 Bash Shell 的数组变量,只是为了方便理解而称呼,其本质还是单变量,Bash Shell 对其进行了扩展从而使得解析时呈现不同效果。

2.4 大小写转换

  • ${str,,}:将字符串全部转为小写

  • ${str^^}:将字符串全部转为大写

2.5 字符串分割

bash shell 下将带有特定分隔符的长字符串分割短字符串数组有很多种方式。

2.5.1 直接修改 IFS 环境变量

对于长字符串分隔符不是空白符(IFS 默认分隔符为空白符:即空格、tab、换行符)的长字符串,可以通过修改 IFS 环境变量来指定字符串分割规则。比如在 bash shell 中输入以下代码:

1
2
3
IFS=:
_str_="hello:world"
for i in $_str_; do echo $i; done

会得到以下结果:

1
2
hello
world

详细参见 IFS变量对加双引号和不加双引号变量的区别对待

2.5.2 使用 ${} 语法

如果不想修改 IFS 环境变量,可以使用上文中的 ${} 语法将长字符串分隔符替换为空白符,这样就可以使用默认的 IFS 环境变量。比如在 bash shell 中输入以下代码:

1
2
_str_="hello:world"
for i in ${_str_//:/ }; do echo $i; done

会得到以下结果:

1
2
hello
world

3. [[ ]] 语法

  • [[ $str1 == $str2 ]]:判定字符串是否相等

  • [[ $str1 != $str2 ]]:判定字符串是否不等

  • [[ $str1 =~ $str2 ]]:判定字符串 str1 是否包含字符串 str2

  • [[ $str1 > $str2 ]]:判定字符串 str1 是否按字典序大于字符串 str2

  • [[ $str1 < $str2 ]]:判定字符串 str1 是否按字典序小于字符串 str2

  • [[ -z $str ]]:判定字符串是否等于空

  • [[ -n $str ]]:判定字符串是否不等于空

4. [ ] 语法

【注】[ ] 不能用来判断字符串相包含;且判断字符串的大小关系时需要使用 `` 转义 < >,防止 bash shell 将其识别为重定向符号;此外,对于 -z-n 选项,字符串必须要用 "" 包裹,不然可能导致奇怪的问题,建议 [] 语法中用到的字符串变量都用 "" 包裹。

  • [ $str1 = $str2 ][ $str1 == $str2 ]:判定字符串是否相等

  • [ $str1 != $str2 ]:判定字符串是否不等

  • [ $str1 =~ $str2 ]:判定字符串 str1 是否包含字符串 str2

  • [ $str1 \> $str2 ]:判定字符串 str1 是否按字典序大于字符串 str2

  • [ $str1 \< $str2 ]:判定字符串 str1 是否按字典序小于字符串 str2

  • [ -z "$str" ]:判定字符串是否等于空

  • [ -n "$str" ]:判定字符串是否不等于空

5. test 语法

【注】test 语法和 [ ] 语法基本一样,也不能用来判断字符串的相包含,< > 也同样需要转义。二者唯一的区别在于,[ ] 是内建的语法,而 test 是调用外在的程序,因此 [ ] 要比 test 更高效。