bash脚本的“参数扩展”中双井号与双百分号的妙用
bash脚本的“参数扩展”中双井号与双百分号的妙用
缘起
在某项工作中,需要将一个cfg
文件(作用类似ini
文件)交给脚本自动化执行,
然而遇到了配置文件的注释问题,体现为以#
开头的行会被符合预期地忽略,但是行中从#
开始的内容并没有被正确地忽略,
这个cfg
的解析是完全通过自写脚本进行的,现有脚本中是怎么处理的这个cfg
配置文件的呢?
1 | while read line; do |
很显然这里只做了#
开头情况的处理,但是缺少了对#
在变量line
内的情况进行处理的语句,没关系,设法写一个出来,
向GPT提问:
now I use shell script
read
has read a line withread line
,I wish to get rid of the
#
and the rest of the line from this line,what should the script be?
它很快给出了一个方案:
1 | !/bin/bash |
于是需求得到了解决,但是问题才刚刚开始,这个句子line="${line%%#*}"
到底做了什么?
探寻
起初,我认为这是某种正则表达式,于是以regular expression
为关键词进行了大量的查找,然而大多数答案只是说%%
在正则表达式中表示一个字面意义的%
,并没有找到满意的答案
最后还是得靠manpage
:man bash
,然后用/
搜索%
,我很快找到了答案,就在“Parameter Expansion”一节中:
1 | ${parameter#word} |
也就是说,这里的%
与#
是bash
解析脚本时使用的,它们作为bash
的参数扩展进行使用;
其中,##
用于清理前缀,%%
用于清理后缀,而且允许搭配*
来选中任意内容;
顺便,单井号#
和单百分号%
也是可以的,但是为了代码的辨识度,还是用双井号与双百分号吧;
实践
作为实验,我们撰写一个shell脚本,命名为syousyuuzai.sh
:
1 | !/usr/bin/env bash |
然后执行它,随便输入一行字符,可以看到如下的效果: 1
2
3➜ ~ ./syousyuuzai.sh
yajuuasdfghjkl
asdfghjkl
同理: 1
2
3➜ ~ ./syousyuuzai.sh
asdfghjklstink
asdfghjkl
前后同时输入也是可以的: 1
2
3➜ ~ ./syousyuuzai.sh
yajuuasdfghjklstink1919810
asdfghjkl
实验中的两个事件
不能把%
与#
组合起来写在同一行中
实验时发现,并不能把%
与#
组合起来写在同一行中,也就是这样line="${line##*yajuu%%stink*}
达到前后同时除臭的效果,如果不信邪的话我们可以写个copy_syousyuuzai.sh
出来:
1 | !/usr/bin/env bash |
这样写脚本的话,运行的时候会变成这样: 1
2
3➜ ~ ./copy_syousyuuzai.sh
yajuuasdfghjklstink
yajuuasdfghjklstink
以及这样: 1
2
3➜ ~ ./copy_syousyuuzai.sh
%stinkasdfghjkl
(空荡荡)
所以,如果要对前后缀同时做匹配,只能分次进行;
*
做前后缀
另外,相信从上面的文本中不难观察到这样的规律,那就是如果把*
作为prefix
的后缀,或者把*
作为suffix
的前缀,都是可以被正常理解并执行的,但是效果是把所有的内容都移除掉了
比如做一个最简单的实验脚本copy_syousyuuzai,sh
:
1 | !/usr/bin/env bash |
第一次,测试没有直接以恶臭数字开头 1
2
3➜ ~ ./copy_syousyuuzai.sh
asdfhyajuujklmn
asdfhyajuujklmn
显而易见地,由于没有直接以恶臭数字开头,且没有写通配符,于是没有通过比对,整个字符串不做筛选就输出了,除臭大失败;
第二次试验,以恶臭数字开头 1
2
3➜ ~ ./copy_syousyuuzai.sh
yajuuasdfghjkl
(空荡荡)
显而易见地,整个字符串全没了,虽然我们失去了整个字符串,但我们除臭了;
第三次试验,我们修改了脚本如下: 1
2
3
4
5
6
7!/usr/bin/env bash
read -r line
line="${line%%*yajuu}"
echo "$line"
然后进行了这样的输入,获取了这样的输出: 1
2
3➜ ~ ./copy_syousyuuzai.sh
asdfghjklyajuu
(空荡荡)
相信不难从中看出规律
掌握了这个技巧,就可以轻松地在脚本中除臭杀菌了;
虽然bash
的Parameter Expansion
特性有很多,不过这次就只学这些了。
关于read
的-r
选项的解释
-r
Backslash does not act as an escape character. The backslash is considered to be part of the line. In particular, a backslash-newline pair may not be used as a line continuation.
也就是说,用-r
时,read
会忽略反斜杠的escape
含义也就是转义含义,会把反斜杠作为句子的一部分处理;
特别地,即便使用了-r
选项,每行输入仍然被视为独立的行,而不能使用反斜杠和换行符来试图将它们组合成单个逻辑行。
Knighthana
2024/04/12