1. 生成从 a 到 z 的字母表
$ echo {a..z}
这一行命令用到了括号展开(Brace expansion)功能,它可以用于生成任意的字符串。{x..y}是一个序列表达式,其中 x 和 y 都是单个字符,这个表达式展开后包含 x 与 y 之间的所有字符。
运行上面的命令会生成从 a 到 z 的所有字母:
echo {a..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. 生成从 a 到 z 的字母表,字母之间不包含空格
$ printf"%c"{a..z}
在这一行命令中,printf 的格式为"%c",代表一个字符(character),后面的参数是从 a 到 z 的字符列表,字符之间以空格分隔。所以,当printf执行时,它依次输出每个字符直到所有字符全被处理完成为止。
下面是执行的结果:
abcdefghijklmnopqrstuvwxyz
$ printf "%c" {a..z} $'\n'
$'\n'代表换行符,这是一个常用的技巧。
echo $(printf "%c" {a..z})
这一行命令用到了命令替换功能:执行printf "%c" {a..z}命令然后用执行的输出替换命令。然后,echo 打印输出结果时会带上换行符。
如果你想要每一行仅输出一个字母,在字符后面增加一个换行符:
$ printf "%c\n" {a..z}
输出:
b
...
z
如果想要快速地将 printf 的结果保存到变量中,可以使用-v选项:
printf -v alphabet "%c" {a..z}
结果会将abcdefghijklmnopqrstuvwxyz保存到变量alphabet中。
类似地,你也可以利用同样的语法生成一个数字列表,例如从1到100:
echo {1..100}
输出:
或者,如果你忘记这种方法,可以使用 seq 命令来做这个事情:
$ seq1 100
3. 输出从 00 到 09 的数字
$ printf"%02d "{0..9}
这里我们又用到了printf 的循环输出功能,这一次的输出格式为"%02d " ,意思是在输出数字的时候,如果不满两位就用0补齐。同时,输出的元素是 0 到 9的列表(括号展开后的结果)。
输出结果:
0 01 02 03 04 05 06 07 08 09
$ echo{00..09}
老版本不包含该特性。
4. 生成 30 个英文单词
echo{w,t,}h{e{n{,ce{,forth}},re{,in,fore,with{,al}}},ither,at}
这是一个滥用括号展开的例子,看看最终输出的结果是什么:
是不是很棒?
你可以通过括号展开生成一组单词或者符号的排列。例如:
echo{a,b,c}{1,2,3}
上面的命令会生成以下结果:a1 a2 a3 b1 b2 b3 c1 c2 c3。首先,它取出第一个括号中的第一个元素a,然后依次与第二个括号{1,2,3}的所有元素组合,生成a1 a2 a3,依此类推。
5. 重复输出 10 次字符串
$ echo foo{,,,,,,,,,,}
这一行命令两次利用了括号展开功能,字符串
foo
与10个空字符串组合,最终生成10分拷贝:foo foo foo foo foo foo foo foo foo foo foo
6. 拼接字符串
$ echo"$x$y"
这一行命令简单地将两个变量的值连接在一起,所以如果x
变量的值为foo
,而y
的值为bar
,则结果为foobar
。注意,这里的"$x$y"是加了双引号的,如果忘记加了,echo会将$x$y当成常规的命令行参数去解析。所以,如果$x在开头包含-,它就变成一个命令行参数,而不是被 echo 输出的内容。
x=-n
y=" foo"
echo $x$y
执行后的输出:foo
相反,正确书写的方式执行后的结果如下所示:
x=-n
$ y=" foo"
$echo "$x$y"
-n foo
不过,如果你要将两个字符串相连的结果赋值给变量,是可以将双引号省略的:var=$x$y
7. 分割字符串
假设变量$str 的值为foo-bar-baz ,如果你想按- 分割成多个段并遍历它,可以使用read 命令,并且设置 IFS 的值为- :$ IFS=- read-r x y z <<< "$str"
这里我们使用read x
命令从标准输入读取内容,分割后并依次保存到x y z
变量中。其中,$x
为foo
, $y
为 bar
, $z
为 baz
。另外要留意的一处是here-string操作符<<<,可以很方便地将字符串传递给命令的标准输入。在这个例子中,$str的内容传给 read 命令的标准输入。
你也可以将分割后的几个字段保存到数组类型的变量中:
$ IFS=- read-ra parts <<< "foo-bar-baz"
在这里,-a
选项告诉read
命令将分割后的元素保存到数组parts
中。随后,你可以通过${parts[0]}
, ${parts[1]}
和${parts[2]}
来访问数组的各个元素,或者通过${parts[@]}
来访问所有元素。8. 逐个字符方式处理字符串
$ while IFS= read -rn1 c; do
# do something with $c
done <<< "$str"
$ while IFS= read -rn1 c; do
# do something with $c
done <<< "$str"
9. 将字符串中的 foo 替换成 bar
$ echo${str/foo/bar}
这一行命令用到了
参数展开
的另外一种形式:${var/find/replace}
,找到$var
变量中的find
字符串,并将它替换成bar
。要想替换所有出现的字符串"foo",请使用${var//find/replace}的形式:
$ echo${str//foo/bar}
10. 检查字符串是否匹配模式
$ if [[ $file = *.zip ]]; then
# do something
fi
$ if [[ $file = *.zip ]]; then
# do something
fi
这一行命令是说,如果$file的值匹配*.zip,则执行if语句里的命令。这种语法下的模式是最简单的通配符(glob pattern)匹配,通配符包括* ? [...]。其中,*可以匹配一个或者多个字符,?只能匹配单个字符,[...]能够匹配任意出现在中括号里面的字符或者一类字符集。
下面是另外一个例子,用来判断回答是否匹配 Y 或者 y:er is Y or y:
$ if [[ $answer = [Yy]* ]]; then
# do something
fi
11. 检查字符串是否匹配某个正则表达式
$ if [[ $str =~ [0-9]+\.[0-9]+ ]]; then
# do something
fi
这一行命令检查$str
是否能够匹配正则表达式[0-9]+\.[0-9]+
,即两个数字中间包含一个点号。正则表达式的规范可以通过 man 手册查询: man 3 regex
$ if [[ $str =~ [0-9]+\.[0-9]+ ]]; then
# do something
fi
12.计算字符串的长度
$ echo${#str}
这里我们又用到了参数展开(也可以叫参数替换)的语法: ${#str}
,它返回$str
变量值的长度。13. 从字符串中提取子串
$ str="hello world"
$ echo ${str:6}
这一行命令通过子串提取操作,从字符串hello world中取到了子串world。子串提取操作的语法格式为${var:offset:length},它的意思是说从变量var中,提取第offset个位置(下标从0开始计算)开始的总共length个数的字符。在我们这个例子中,忽略了length,默认会返回所有剩余的字符。
下面是另外一个例子,返回$str变量中第7、8位置的两个字符:
$ echo${str:7:2}
输出结果为or
。14. 转换成大写
$ declare -u var
$ var="foo bar“
$ echo $var
FOO BAR
注意,-u
选项也是在 Bash 4 新版本中引入的功能,在低版本下是没有的。类似地,你还可以使用 Bash 4 提供的另外一种参数展开语法${str^^}
,也可以将字符串转换成太写的格式:$ str="zoo raw"
$ echo ${str^^}
15. 转换成小写
$ declare -l var
$ var="FOO BAR"
同上面一条类似,-l
选项声明变量的小写属性,使得其值转换成小写的格式:$ declare -l var
$ var="FOO BAR"
$ echo $var
foo bar
同样,只有 Bash 4 以及以上的版本才支持-l选项。另外一种方式是使用参数展开语法:
$ str="ZOO RAW"
$ echo ${str,,}
我补充一句,如果是 Bash 4 以下,还是老老实实地用tr命令就可以了。