UpDate 2018-10-06
Author unnam3d
Tip Please feel free to contact me via mail above for any confusion or suggestions
[零] bash特性一览
首先我们先大概了解一下bash都支持哪些特性:
- 命令行编辑
- 命令历史
- 命令别名
- 命令替换
- 文件名通配(globbing)
- I/O重定向
- 管道
- 命令行展开
- 使用变量
- 脚本编程
我们这里只介绍前7种特性
[壹] 命令行编辑
命令光标跳转(常用)
- Ctrl+a: 跳到命令行首
- Ctrl+e: 跳到命令行尾
- Ctrl+u: 删除光标至命令行首的内容
- Ctrl+k: 删除光标至命令行尾的内容
- Ctrl+l: 清屏(当然也可以用命令 clear 直接清屏)
环境变量
- PATH:命令搜索路径,shell中的命令都是通过这一变量来搜索系统中存在的命令具体位置,并加以执行。查看该变量可以使用
echo $PATH
- HISTSIZE:命令历史缓冲区大小,能够保存多少条历史命令。查看该变量可以使用
echo $HISTSIZE
命令补全
在Linux系统中,命令补全一共有两种机制:
- 路径补全:该种补全是我们在进入目录的过程中使用[Tab]按键来进行路径名称的补全
- 命令补全:该种补全是我们在执行命令的过程中使用[Tab]按键来进行命令名称的补全
[贰] 命令历史
查看命令历史:history
history -c
: 清空命令历史
history -d 500
: 删除命令历史中的第500个命令
history -d 500 3
: 删除命令历史中第500个命令开始的3个命令
history -w
: 保存命令历史至历史文件中
命令历史的使用技巧
!n
: 执行命令历史中的第n条命令
!-n
:执行命令历史中的倒数第n条命令
!!
:执行上一条命令
!string
:执行命令历史中最近一个以指定字符串(string)开头的命令
!$
:引用前一个命令的最后一个参数(该目的还有另一种实现办法,就是按下ESC后松开,按个 . 就可以把前一个命令的最后一个参数引用出来,或者按住Alt后不送按 . )
[叁] 命令别名
命令别名就是指给我们的命令起另外一个名字,然后我们就可以既使用命令本身,还可以使用命令别名。
以linux下的清屏命令做一个示例
Linux下的清屏命令为 clear
然而在Windows下的清屏命令为 cls
如果我们想要实现在Linux下 cls
同样也可以达到清屏的效果,那我们就可以在shell中输入 alias cls='clear'
来进行clear
的命令别名定义。
取消别名定义
既然可以定义别名,如果想要撤销这个操作,在shell中使用 unalias cls
就可以了
通用公式
因此我们在别名定义的时候要用到的普遍情况的公式就是 alias CMDALIAS='COMMAND [option] [arguments]'
如果我们要撤销别名定义 unalias CMDALIAS
即可
NOTICE
-
别名的定义,与变量的定义一样,都是shell的特性,因此,如果shell关闭了,再次启动shell后,别名就不存在了,所以我们定义的别名只在当前shell进程的生命周期有效。
-
这里我们会遇到一个问题,如果我们对COMMAND进行了别名定义,定义后的别名也叫COMMAND,这个别名是包括了[option]和[arguments]的,那这时候我们使用COMMAND的时候,其实使用的是包括option和arguments的COMMAND,那我们想使用最原始的不包括option和arguments的COMMAND时候,我们使用
\COMMAND
就可以了 -
不带任何参数和选项的
alias
命令可以显示当前系统上定义的所有别名。
[肆] 命令替换
把命令中某个子命令替换为其执行结果的过程。
示例 1
$ echo "the current directory is $(pwd)"
the current directory is /etc/sysconfig
$ cd /etc
$ echo "the current directory is $(pwd)"
the current directory is /etc
在示例中,所谓命令就是指echo
这条命令,所谓子命令就是指$(pwd)
这条命令。命令替换的含义一目了然。
示例 2
$ touch ./file-$(date +%F-%H-%M-%S).txt
该条命令就是在当前目录下创建一个文件名为file加当前日期和时分秒的txt文件。比如file-2018-09-30-22-05-35.txt
NOTICE
示例中我们使用了$()
来完成了命令的替换,其实还可以使用反引号来实现同样的目的——命令的替换。比如示例一中就可以修改命令为:
$ echo "the current directory is `pwd`"
the current directory is /etc/sysconfig
# bash支持的引号有三种类型(深入理解请移步至“变量替换”的部分):
# 1. ``: 反引号,命令替换
# 2. “”: 弱引用,可以实现变量替换
# 3. '': 强引用,不完成变量替换
[伍] 文件名通配(globbing)
这其实是一种命令行展开的机制(波浪线展开,花括号展开)。用于文件名通配的常用符号有* ? [] [^]
;
- '*' : 匹配任意长度的任意字符
- '?' : 匹配任意单个字符
- '[]' : 匹配指定范围内的任意单个字符
- '[^]' : 匹配指定范围外的任意单个字符
# 具体如何理解呢
$ touch 'a123' 'cd6' 'c78m' 'c1 my' 'm.z' 'k 67' '8yu' '789'
$ ls
a123 cd6 c78m c1 my m.z k 67 8yu 789
$ ls *7*
c78m k 67 789
$ ls ?8*
789
$ ls ??8*
c78m
$ ls [a-z]*[0-9]
a123 cd6 k 67
$ ls [a-z]*[^0-9]
c78m m.z
# 解释:
# `ls *7*` 关于 * 的匹配任意长度,可以看到文件名中只要包含 7 ,就被打印了出来
# `ls ?8*` 关于 ? 的匹配单个字符,可以看到文件名中包含 8 且 8 前面只有一个字符的被打印了出来
# `ls ??8*` 而两个 ? 就打印出来了 8 前面包含两个字符的文件名
# `ls [a-z]*[0-9]` 在[]之中的符号表示一个符号的范围集,只有在这个范围内的才被打印了出来
# `ls [a-z]*[0-9]` 与[]不同的是,[^]表示范围集之外的符号,所以以数字结尾的文件名就没有被打印出来
NOTICE
关于[]
和[^]
的几个字符集合表示法:
- [:space:] 表示空白字符
- [:punct:] 表示标点符号
- [:lower:] 表示小写字母
- [:upper:] 表示大写字母
- [:alpha:] 表示大小写字母
- [:digit:] 表示数字
- [:alnum:] 表示数字和大小写字母
以上的这些[:内容:]
都是集合,而我们在使用的时候,一定是这样子的:[[:内容:]]
,比如表示数字可以是[0-9],而与[0-9]等价的其实是:[[:digit:]]
[陆] I/O重定向
关于I/O的重定向,在系统的设定中,先来了解三个概念:
- 默认输出设备:标准输出,STDOUT,1
- 默认输入设备:标准输入,STDIN,0
- 标准错误输出:STDERR,2
后面的数字分别代表Linux中相应的标识符,我们应当注意的是,输出包括标准输出以及错误输出,但这两种输出的数据流是不同的,可以理解为,彼此的输出互不干扰,都是独立于彼此。对于标准输出,我们正确执行了命令之后而产生的输出信息,将会通过标准输出来展示,而在执行命令的过程中,如果有了报错,那么这个错误信息将会通过错误输出来显示。它们两个在进行重定向的时候是由不同的标识符来识别的,也就是1和2来进行识别。
系统默认的标准输入设备为键盘,而标准输出和错误输出的设备都是显示器。
那如果我们不想使用系统的默认设置怎么办?
我们可以将我们的输入输出进行重定向。实现方法包括:
> 覆盖输出
>> 追加输出
set -C
禁止对已经存在文件使用覆盖重定向
set +C
关闭上述功能>| 在进行了
set -C
的基础上实现强制覆盖输出2> 重定向错误输出的覆盖方式
2>> 重定向错误输出的追加方式
&> 重定向标准输出或错误输出至同一个文件
< 输入重定向
<< Here Document
具体实现示例如下:
# touch >> tmpfile.txt << EOF
> The first line
> The second line
> EOF
# ls /var >> tmpfile.txt
# ls /var > tmpfile.txt
# set -C
# ls /etc > tmpfile.txt
-bash: /root/tmpfile.txt: cannot overwrite existing file
# ls /etc >| tmpfile.txt
# set +C
# ls /ect
ls: /ect: No such file or directory
# ls /ect 2> tmpfile.txt
# ls /ect 2>> tmpfile.txt
# ls /ect &> tmpfile.txt
# cat < tmpfile.txt
ls: /ect: No such file or directory
关于上面的代码解释:
首先执行的是<<
的Here Document功能,也就是说,当出现<<
的时候,该符号后面跟的字符将会作为一个结束标识(通常为END或者EOF,end of file),就像上面的代码示例一样,将会出现一个>
标识提醒用户进行文档的编辑输入,当标准输入接收到了结束标识,如上面的示例,也就是接收到了EOF之后,就会结束文档的编辑,并进行标准输出,但上面并没有进行标准输出,这是因为在代码中间有一个重定向>> tmpfile.txt
,将标准输出的内容重新定向输出到了文件tmpfile.txt中了。
随后进行的ls /var >> tmpfile.txt
将目录/var
下的子目录名都追加到了文件tmpfile.txt
中去了,也就是说,此时的tmpfile.txt
文件中还保留着上一步我们手动输入的两行文本。接着执行的ls /var > tmpfile.txt
也是将目录/var
下的子目录名写入到了文件tmpfile.txt
中去了,但此时不是追加,是覆盖,也就是说,此时的tmpfile.txt
文件中只保留了目录/var
下的子目录名,而这些子目录名是新写入的子目录名,之前的子目录名包括我们一开始写入的文本都被此时的子目录名进行了覆盖。
下一条我们执行的命令为set -C
,这条命令限制了我们上一条命令的覆盖,我们此时一般情况下只能进行追加写入,也就是使用>>
符号,如示例代码一样,此时我们使用覆盖写入的话,将会出现报错。但如果在这种情况下,我们一定要使用覆盖写入,我们就可以如示例代码一样,使用ls /etc >| tmpfile.txt
进行强制性的覆盖写入,因此,我们可以看出,set -C
的作用是为了确保我们不会手残误使用覆盖写入而丢掉重要的信息。而当我们想要撤销禁止覆盖写入的时候,我们执行一次set +C
即可。
然后我们执行了ls /ect
命令,出现了报错,报错提示我们没有/ect
这个目录,这是自然的,这是为了方面后面的错误输出的解释。
我们执行ls /ect 2> tmpfile.txt
,就会将我们的报错信息重定向覆盖写入到文件tmpfile.txt
中去,此时,我们的文件中只有一行报错信息。随后我们执行ls /ect 2>> tmpfile.txt
,这时,我们的报错信息再次被重定向写入到了文件tmpfile.txt
中去了。但此时是追加写入,也就是说,此时,我们的文件中有两行报错信息了。
然后我们执行的ls /ect &> tmpfile.txt
就是将我们的标准输出和错误输出都重定向到了同一个文件tmpfile.txt
中去了。
关于标准输入的重定向,也就是说,我们将我们的文件内容作为标准输入传输给了系统。以cat < tmpfile.txt
具体来说,就是我们将此时tmpfile.txt
中的两行报错信息作为输入传输给了系统。
[柒] 管道
命令的管道:把前一个命令的输出,作为后一个命令的输入。
管道的意义:组合小命令完成复杂的任务。形如:
命令1 | 命令2 | 命令3 | ...
# 只输出文本的行数
$ wc -l /etc/passwd
45 /etc/passwd
$ wc -l /etc/passwd | cut -d' ' -f1
45
# 统计 /usr/bin/ 目录下的文件个数
$ ls /usr/bin | wc -l
# 取出当前系统上所有用户的shell,要求:每种shell只显示一次,并且按顺序进行显示
$ cut -d: -f7 /etc/passwd | sort -u
# 思考:如何显示 /var/log 目录下每个文件的内容类型
$ cd /var/log
$ file `ls /var/log`
# 取出 /etc/inittab 文件的第6行
$ head -6 /etc/inittab | tail -1
# 取出 /etc/passwd 文件中倒数第9个用户的用户名和shell,显示到屏幕上并将其保存至 /tmp/users文件中
$ tail -9 /etc/passwd | head -1 | cut -d: -f1,7 | tee /tmp/users
user3:/bin/tcsh
$ cat /tmp/users
user3:/bin/tcsh
# 显示 /etc 目录下所有以 pa 开头的文件,并统计其个数
$ ls -d /etc/pa* | wc -l
7
# 不使用文本编辑器,将 alias cls=clear 一行内容添加至当前用户的 .bashrc 文件中
$ echo "alias cls=clear" >> ~/.bashrc
网友评论