本文是快乐学习Linux的第三篇,是基于Windows的Linux子系统(WSL)进行操作的。
适用于学习Linux系统中shell特性和键盘操作的读者。
字符展开
每当我们在bash中输入一串命令,bash就会在执行命令之前将命令进行展开
操作。
我们可以使用echo
来显示一行文本:
1 | echo This is a test. |
那么不出意外将会输出结果This is a test.
,当我们输入:
1 | echo * |
就会出现意外了😂,结果是根据通配符含义的“*
”匹配当前文件夹的任何内容,而不是我们想要打印的“*
”,这就意味着我们的命令有了自己的想法,想要按照自身的规则先进行展开成完全态,再执行命令。
路径名展开
以上这样的展开被称为路径名展开,例如打印当前目录所有的以l
开头的文件或目录:
1 | echo l* |
但是无法显示隐藏文件的展开路径,但是办法总比困难多
。我们进行尝试:
1 | echo .* |
几乎成功了,但是存在了不需要的.
和..
。换个方法:
1 | ls -d .[!.]?* | less |
仅展示以.
开头,第二个符号不是.
,并且至少再包含一个符号,之后可以紧跟任意多个字符。
我们可以发现,对于文件和目录进行打印输出的时候,使用echo和ls的结果似乎差不多,但是细心的话,我们可以发现,ls文件名之间的间距是根据
制表符
规定的,而echo每两个文件名之间的间距是一个空格
,其中原因也是shell的命令展开。
算数表达式展开
算术表达式是形如$((expression))
,表达式中不在意空格,并且可以嵌套存在,需要注意的是除法“/
”,由于表达式仅支持整数操作,所以除法操作得到的结果是舍尾的整数,想要获得余数可以使用“%
”进行操作。
花括号展开
花括号是为了获得多个具有一定重复规律的字符串而出现的,举个例子💡:
1 | echo hello-{R,G,B}-world |
则结果会生成hello-R-world hello-G-world hello-B-world
,或者想要生成2019-01至2021-12的文件夹来存放相应内容,可以使用:
1 | mkdir {2019..2021}-0{1..9} {2019..2021}-1{0..2} |
其中花括号内可以包含字符串列表,整数区间和字符区间,区间使用..
来表示区间范围,其中字符区间全部是大写或全部是小写的时候没有任何问题,但是在大写到小写的区间内,由于ASCII
码的原因,会存在几个过度的字符,这需要注意一下。
同样的,花括号也可以嵌套
。
参数展开
参数展开在此处简要体提及,之后会在shell脚本中详细阐述。
我们查看一下可以使用的有效参数:
1 | printenv | less |
这些参数可以使用 $
符号进行调用,来显示参数对应的值:
1 | echo $USER |
命令替换功能也能将命令的输出作为一个展开模式来使用,例如将找到cp目录的结果作为展开的参数给ls进行打印:
1 | ls -l $(which cp) |
同样的,管道线也适用参数展开,在旧版bash中也可以使用ls -l `which cp`
达到同样效果。
引用
这里的引用指的是一种引用机制,用来有选择的禁止不需要的展开。什么叫做不需要的展开,如下:
1 | echo This is a Test |
但是结果是This is a Test
,中间想要的空格被自动展开后再进行单词分割(空格、制表符、换行符等界定符)而失去了。再比如:
1 | echo The total price is $100.00 |
但是结果却是The total price is 00.00
,原因是$1
被视作名为变量1的参数。
为了禁止这些不需要的展开,所以出现了引用机制,这里有两种类型,一种是双引号,一种是单引号。
使用双引号的时候,shell使用的一些特殊字符将会失去其特殊含义,单词分割,路径名展开,波浪线展开(用户名)和花括号展开都会被禁止。
例如这个存在空格的破损文件名word count.txt
,正常情况下ls的时候会认作两个参数,而使用双引号的时候就会关闭单词分割,从而视作一个参数来正确读取,并且可以使用mv进行重命名。
1 | ls -l "word count.txt" |
再比如打印日历:
1 | echo $(cal) |
这就是被单词分割的后果!可以尝试加上双引号,一下子好看了有木有😆!
但是对于$,\和`
并不会被禁止,也就是意味着参数展开,算术展开和命令替换仍然会被执行。
这就需要使用单引号来禁止全部展开,通过以下对比进行说明:
1 | echo text ~/.txt {a,b} \$100 $(echo ~) $((2+2)) $USER `which cp` |
对于转义字符“
\
”有一个很有趣的东西,echo加上-e
能够解释转义字符,例如,10秒后响铃⏰:
1 sleep 10; echo -e "Time's up \a"
键盘的高级操作
这里再附带说一下键盘有关的高级操作,命令行的目标是懒惰
,各种简短而丰富的命令是为了更少的敲击键盘和无需使用鼠标,来专注于工作本身。
在命令行中存在很多的按键,来快速编辑,例如:
按键 | 作用 |
---|---|
Ctrl-a | 光标移动到行首 |
Ctrl-e | 光标移动到行尾 |
Alt-f | 光标向前移动一个字 |
Alt-b | 光标向后移动一个字 |
Ctrl-l | 清空屏幕,与clear相同 |
Alt-l | 光标至字尾的字符转化为小写字母 |
Alt-u | 光标至字尾的字符转化为大写字母 |
自动补全
自动补全是shell一个很方便的方法,Linux系统不一样可能机制不同,WSL中是只有一个匹配的则敲击一下tab
键即可,存在多个匹配条件,则连续敲击两下tab
键显示。
历史命令
使用上下键就可以浏览之前输入过的历史命令,不过还可以使用history进行查看:
1 | history | less |
在每个历史命令中,每行会有一个序号,我们在命令行直接输入!80
就可以直接执行第80行的命令了。
使用Ctrl-r
可以进入搜索模式,输入部分命令内容,继续按下Ctrl-r
就可以从现在到过去进行历史命令匹配了。
个人收获
总之,Linux中的shell是一个很有趣的事物,准确的理解其中展开和引用机制可以很好的发挥shell的能力。对于最后提及的键盘快捷键,暂时来说只是锦上添花,更多的还是关注命令本身。