shell 用法简介
通过阅读本章,你将会了解到以下几项内容。
- 了解 Linux Shell
- 打开 Shell 终端的方法
- Bash 语法简介
- Bash 自动补全和输入历史
- 命令的拼接和拓展
- 变量和简写
- 如何使用理解帮助文档
作为普通人,使用电脑除了窗口图形界面外,还有个叫终端的字符界面。Linux 中通过在终端敲击字符跟电脑交互,这玩意就叫做 shell,并且相比图形界面(Graphic User Interfaces, GUI),通常 shell 功能更加强大。
深度系统默认采用的 shell是 bash
(Bourne Again Shell),继承兼容于 UNIX 早期的 Bourne shell (作者:Stephen Bourne, 命令名为 sh
),当然两者还是有那么一点点不同的。曾经有一段时间深度默认 shell 是 zsh
,经群众投票又改回 bash
了,其实那次投票是我发起的。
除此之外,类 UNIX 系统还有其他的一些 shell,比如 ksh
,csh
,tcsh
,ash
,当然还有非常好用的 zsh
等,有兴趣的可以查相关资料。
用 shell 有啥好处?
为了装吗?不见得。
刚接触 Linux 桌面发行版的时候,按着教材说的,就在学习终端哦。遇到糟糕的 Linux 图形界面卡住了、死机啦,不得不网上搜索解决方案,能不用终端 shell 吗?作为小白,遇到各种各样的问题,查询询问的结果,大神的建议,也多半是在 shell 下解决啊,这叫不得不用。
后来慢慢脱离低级小白,开始喜欢上终端远程连接其他 Linux 电脑,享受这种快捷方便的操作。再后来终端下用 vim 写代码,键盘翻飞,似乎成为了一种享受。
再后来,开始接管一些服务器,开始学习计算机安全知识,似乎更离不开了 shell 了。似乎儿时那种黑客的感觉,只有通过 shell 才可以实现哦。
其实 Windows 下也有 DOS 和 CMD 这类字符操作界面,不过功能弱爆了, powershell 可能功能强大一些,有兴趣的可以查相关资料,这里不说了。
深度操作系统下 shell 简介
按照《Linux Bible》的说法,有三种方式打开 shell 操作界面,分别叫shell提示符(shell prompt),终端窗口(Terminal window),和虚拟控制台(virtual console)。我感觉深度操作系统就后面两种,第一种说是登录的时候出现的那种字符界面,不太容易遇到,暂且跳过。
深度操作系统登录后,进入桌面,按下 Ctrl + Alt + T
(或F4
或Alt + F2
打开终端雷神模式,这个版本不同有所变化)会弹出深度终端窗口,这个软件在第三章已经作了介绍。可用于 shell 练习。对于虚拟控制台,可以通过按下 Ctrl + Alt + Fi
其中 Fi
表示 F1,F2,...,F6
,分别弹出六个虚拟控制台,对于不同的电脑,可能Ctrl + Alt + F1
或者Ctrl + Alt + F7
打开的是图形界面(GUI)。
下面以深度终端窗口为例来说明。
打开深度终端的方式
- 快捷键的方式:按下
Ctrl + Alt + T
或者 F4
或 Alt + F2
,弹出深度终端。
- 图形方式:按下
super
键,或者点击左下角的图标,会弹出已安装软件图标,找到深度终端的图标,点击即可。或者在最上面搜索框中搜索终端。
- 鼠标右键:在桌面上,右击选择在终端中打开,也可以进入深度终端。
打开终端后,右击窗口内部,选择设置,可以对终端的一些外观属性配置。
深度终端的显示说明
在每行命令行开头会如下显示,
litianci@litianci-pc:~$
解释
litianci@litianci-pc:~
表示用户名和电脑名,并用@
隔开,:
后是当前工作目录~
,~
是主目录的意思,因为古老的键盘中~
和Home
是同一个按键,所以就用 ~
代替主目录了。
$
默认表示普通用户, #
默认表示 root 根用户。
- 这个显示说明是可以修改的,见后文 @ref() 小节。
- 在
$
或者 #
后输入相关的命令字符。
$
表示正在用普通用户权限运行后面的命令,而 #
则表示正在使用 root 根用户的权限。通常只有涉及重大核心系统功能的地方才需要用到root根用户权限。根据放权的最小权限原则,在普通用户权限可做的事情,建议不要使用 root 根用户权限。
牛刀小试
下面命令行首标志 $
和 #
区分运行权限,其他显示信息略。输入结果行没有行首标志,每行会有序号方便区分。
$ whoami
litianci
$ who -H
名称 线路 时间 备注
litianci tty1 2018-09-20 09:07 (:0)
$ grep litianci /etc/passwd
litianci:x:1000:1000::/home/litianci:/bin/bash
解释
whoami
列出当前用户名
who -H
列出信息更详细,-H
表示显示头部标题列,第二行显示当前登录用户名litianci
,当前登录线路终端tty1
,当前用户登录系统时间2018-09-20 09:07
,以及备注(:0)
,该备注啥意思暂时没有找到,是不是图形界面的意思,待后续找资料。
- 第6行,是在/etc/passwd文件内查找该用户,在第7行最后
/bin/bash
,表示该用户默认的shell类型。实际操作时请把litianci
换成你的账户名。
$ date
2018年 09月 21日 星期五 16:45:08 CST
$ pwd
/home/litianci
$ hostname
litianci-PC
$ hostname -I
192.168.43.45
$ ls
Desktop Documents Downloads Music Pictures Videos
解释
date
命令,无选项无参数时,输出当前日期、星期、时间和时区。其中CST表示中国标准时区,也即东八区。不过CST这个简写有歧义,代表如下几个时区,
- CST Central Standard Time (USA) UT-6:00
- CST Central Standard Time (Australia) UT 9:30
- CST China Standard Time UT 8:00
- CST Cuba Standard Time UT-4:00
pwd
命令输出当前工作目录。
hostname
命令,无选项无参数时输出本机名称,如果加选项-I
,则输出本机IP地址。
ls
命令列出当前目录下所有的可见文件及文件夹。
命令语法结构
为了丰富命令的功能,命令常常带多种选项及参数的,这里区分两个名称,
- 选项(Options):是调整命令执行行为的开关,即,选项不同决定了命令的显示结果不同;
- 参数(Arguments):是指命令的作用对象。
选项(Options)
选项分为长选项和短选项。下面示例中,-l -a -t
就是短选项,
$ ls -l -a -t
$ ls -lat
解释
-l
(long listing),宽列,较长格式列出信息,-a
(all)列出所有文件(夹),包括隐藏文件(夹),-t
(time)按时间排序。
- 短选项一般使用
-
短横线引导。也有不带-
的,比如ps aux
,这类叫BSD风格的选项。
- 当有多个短选项时,各选项之间使用空格隔开。
- 有些短选项可以组合,比如
-l -a -t
可以组合为-lat
。
- 有些短选项需带参数,比如-L 512M,则不便跟其他短选项组合,但如果位于最后一个也是可以组合的。比如
tar cvf deepin-bible.tar ~/deepin-bible
,各选项说明见后面8.2.5节解释。
$ ls --hide=Desktop
Documents Downloads Music Pictures Videos
解释
--hide=Desktop
,不列出 Desktop 这一文件夹。
- 长选项一般使用
--
两个短横线引导,而且后接完整英文单词。
- 长选项通常不能组合。
- 长选项如果需要参数,一般用
=
,比如--size=1G
。不过也有用空格隔开的,比如 pandoc test.md --to latex
表示使用pandoc
命令把test.md文件转化为latex
格式。
以上均为常见格式,具体使用方法,还需查看相关命令的帮助,比如cmd --help
,获得正确用法。
参数(Arguments)
上面讲述选项有所介绍,通过空格或者等号传入参数,也有直接通过空格缀在命令后面的。比如,
$ cd /media/litianci/data/
跳转到/media/litianci/data/ 文件夹。
更多例子
为了方便大家上手,再多介绍几个例子。
$ tar cvf deepin-bible.tar ~/deepin-bible
$ uname
$ uname -a
$ date
$ date +'%Y-%m-%d'
2018-09-21
$ date +'%A, %B %d, %Y'
星期五, 九月 21, 2018
$ id
uid=1000(litianci) gid=1000(litianci) 组=1000(litianci),7(lp),24(cdrom),27(sudo)
$ who -aH
名称 线路 时间 空闲 进程号 备注 退出
系统引导 2018-09-21 15:54
运行级别 5 2018-09-21 07:54
litianci + tty1 2018-09-21 07:55 旧�� 3820 (:0)
登录 tty2 2018-09-21 21:48 21868 id=tty2
解释
- 第1行命令,
c
(create)表示创建压缩文件,v
(verbose)表示详细信息,f
(file)表示创建的文件为deepin-bible.tar
;这是典型的组合命令,而最后一个组合选项f
带参数deepin-bible.tar
。~/deepin-bible
置于最后,为待压缩文件夹。这样本条命令实现了对文件夹的压缩。
- 第2、3行,
uname
命令,显示系统名字,-a
(all)选项,表示显示系统所有信息。
- 第4、5、7行,输出不同格式的日期时间格式。详细说明可以参考
date --help
的说明。有兴趣的可以多试试。注意系统语言对显示结果的影响。
- 第9行,
id
,常用于核实自己账户编号信息。
- 第11行,
-a
表示显示较多信息,具体信息参考who --help
说明。-H
已经在前面说明,显示列标题。
由于采用系统默认的中文,深度终端自作聪明的翻译,有些内容乱码。为了方便叙述,后面把终端显示改为英文的,也即在~/.bashrc文件加上所用语言选项,并执行。
$ echo LANG=en_US >> ~/.bashrc
$ source ~/.bashrc
或者直接修改所有用户的 bash 信息,sudo gedit /etc/profile
,修改最后一行的LC_ALL=C
为LC_ALL=en
。
回头再看who -aH
命令,就清爽了。当然需要你对英文有一定理解了。
$ who -aH
NAME LINE TIME IDLE PID COMMENT EXIT
system boot Sep 21 15:54
run-level 5 Sep 21 07:54
litianci + tty1 Sep 21 07:55 old 3820 (:0)
LOGIN tty2 Sep 21 22:02 25289 id=tty2
找找命令文件在哪里
相信大家都听说过一切皆文件的Unix哲学,其实很多命令都是可执行文件,是可以查看这部分命令的文件的。比如bash
位于/bin/bash,那么命令bash
和/bin/bash
就是相同的。为啥我们可以简写为 bash
呢?是因为shell有PATH
(路径)这个环境变量。众多已知目录存放在 PATH
变量里,shell 遇到这些没有目录路径的命令,在查找其他默认命令集合无果后,就会去PATH
路径变量里各目录下去找该文件名,从左到右查找,找到后就执行命令,找不到就会报错。那么你电脑上的 PATH
是啥呢?
$ echo $PATH
/home/litianci/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin
一般命令,都存放在 /bin,/usr/bin,或 /usr/local/bin 文件夹。一般管理相关的命令放在 /sbin 和 /usr/sbin 文件夹,也有一些 GNU/Linux 发行版放在非常规文件夹下。这些文件夹以英文冒号 :
隔开。最后无冒号。
而最开始的/home/litianci/bin文件夹是自己所用的目录。你可以对 PATH
修改,添加某些你常用的命令文件夹。配置方法见后面叙述。
不过这里存在安全隐患,如果罪恶黑客侵入你系统,无权修改你系统级别的文件,但是有能力在你的文件夹下创建可执行文件,比如/home/litianci/bin,跟系统文件夹的命令同名,你以为你用的是系统的命令,并输入密码,却不料执行的是黑客想要的命令。对于系统管理员,建议输入命令的时候,把目录带全,避免这类权限泄露。
下面总结 shell 查找命令位置的优先级别。
- 别名(aliases)。别名就是某命令包含或不包含一系列选项的集合,便于输入。后面@ref()小节详述。
- shell 保留字(Reserved word),构建 shell 脚本的基础,比如
do
,while
等。见后面@ref()章详述。
- 函数(function),shell 中创建的命令集合。
- shell 内置命令(built-in),这些命令在文件系统中是找不到的,比如
cd
改变工作目录,exit
退出shell,echo
输出文本,history
查看之前命令运行历史,pwd
显示当前工作目录,还有set
和type
等。
- 文件系统中可以执行的文件。比如
PATH
变量中那些目录下的文件等。
你输入的命令是哪个呢?
在 bash 内,使用
$ type ls
ls 是 `ls --color=auto` 的别名
可以看到你的命令是哪个命令。为了显示全部同一名称的命令,可以加-a
选项。
$ type -a ls
ls 是 `ls --color=auto` 的别名
ls 是 /bin/ls
$ type -a cd
cd 是 shell 内建
可以看到有两个ls
命令。cd
是内置命令(翻译有差距)。其他shell也可用which
命令查找当前正在使用的命令是哪一个。
此外还有可以找命令文件位置的which
和locate
(深度系统未自带),有兴趣的可以去找找资料。
命令快捷编辑
方便编辑的快捷键
如果命令不足够快捷,那还不如鼠标点点点呢。提到编辑器,在 Linux 世界就跳不过编辑器之神(vim)和神之编辑器(emacs)的争论。在这里不探讨孰是孰非。而 bash 默认的编辑方式就是这两者之一的 emacs 模式。如果喜欢 vim 的编辑方式,也可用设置为 vim 模式。在 ~/.bashrc文件末尾加上 set -o vi
,下次启动终端的时候,就是 vi 模式了。所谓模式,也是为了方便编辑,提供一些快捷键等。我最喜欢用的快捷键,就是 Ctrl+A
跳到行头,Ctrl+E
跳到行尾和Ctrl+L
清屏了。其他的都是键盘上下左右键切换,懒得记录这些快捷键。不过还是把这些快捷键列在下面吧。
光标跳转快捷键说明
Ctrl+F |
向前一个字符 |
Ctrl+B |
后退一个字符 |
Alt+F |
向前一个单词 |
Alt+B |
向后一个单词 |
Ctrl+A |
跳到该行行首 |
Ctrl+E |
跳到该行行尾 |
Ctrl+L |
清空屏幕内容 |
注:清屏,只是把内容放到屏幕上方,并加入了很大一片空白,滚动鼠标还可以看到刚刚输出的内容。
编辑快捷键说明
Ctrl+D |
删除当前字符 |
Backspace |
删除前面字符 |
Ctrl+T |
前后字符换位 |
Alt+T |
前后单词换位 |
Alt+U |
当前单词大写 |
Alt+L |
当前单词小写 |
Alt+C |
选中字符大写 |
Ctrl+V |
插入特殊字符 |
注:每个快捷键具体动作,还需要多加练习记忆。对于插入特殊字符的,Ctrl+V
比如插入表格键Table
,则Ctrl+V+Tab
,经测试不是那么回事。
复制粘贴快捷键说明
Ctrl+K |
剪切光标到行尾的全部字符,含光标所在字符 |
Ctrl+U |
剪切光标到行首的全部字符,不含光标所在字符 |
Ctrl+W |
剪切光标前到当前单词词首的字符,不含光标 |
Alt+D |
剪切光标后到当前单词词尾的字符,含光标 |
Ctrl+Y |
粘贴到光标前 |
Alt+Y |
循环显示刚刚剪切的内容,待你确定粘贴对象 |
Ctrl+C |
删除整行 |
重输之前的命令
参考网页:
bash 是会记录你已经输入命令的。使用history
命令,可以查看相关内容。常见用法如下。
- 上下键查看以前的命令(包括
Ctrl+N
、Ctrl+P
快捷键);
history 8
查看最近八条命令记录;
history
查看最近$HISTSIZE
条命令记录
!n
调用第n条命令,n是一个正整数;
!!
调用上一条命令;
!字符串
调用最后一以该字符串开头的命令。
本人感觉以上命令不太实用。除非你知道刚刚是啥命令,才方便操作。Matlab 软件的命令记录就很好查询。输入部分字符就可以把所有以该字符开头的命令记录一次列出。对于 bash,类似的功能非常鸡肋。在空行内按下快捷键Ctrl+R
,输入部分字符,会查找相关命令记录,符合你需求,回车,就直接运行了。丝毫不给你重新编辑的机会,也无法上下翻阅,难用至极。另外还有快捷键Ctrl+S
在深度操作系统下,不能用。另外两个Alt+P
,Alt+N
有心情的可以试试。如果哪位有好的用法,欢迎提供。
默认记录1000条命令,但是事实上记录文件里记录的命令条数可能超过此数值。可以输入如下命令查看,
$ echo $HISTSIZE
1000
$ echo $HISTFILE
/home/litianci/.bash_history
解释
- 第1行命令,查询命令记录条数,显示为1000。变量
$HISTSIZE
用于设置记录条数。
- 第3行命令,查看命令记录文件,本机为/home/litianci/.bash_history。
如果我们想修改记录条数,或者记录文件,可以/etc/profile修改相应的变量。下面以修改记录条数为200进行讲解。
$ sudo sed -i 's/^HISTSIZE=1000/HISTSIZE=200/' /etc/profile
$ source /etc/profile #使其立即生效
上方命令使用的是sed
输入的,您可以以其他方式,在文件/etc/profile加上一行HISTSIZE=200
。并保存退出。
对于系统管理员来说,可能保存这些操作记录很危险。你可以设置零记录,或者把记录文件设置为垃圾箱。
HISTSIZE=0
,HISTFILE=/dev/null
自动补全
参考网页:
在Linux命令行下,输入字符后,按两次Tab
键,shell就会列出以这些字符打头的所有可用命令。如果只有一个命令匹配到,按一次Tab
键就自动将这个命令补全。比如,想更改密码,但只记得这个命令前几个字母是pass
。这时候,按Tab
键,shell
就自动输出 passwd
命令,非常方便。
有些发行版,比如centos
最小化安装,常常只能补全命令名和文件名,则需要安装补全增强软件包bash-completion
。而一般补全的内容有如下几种。
- 命令(Command)别名(alias)和函数名(function)。常规字符输入,敲一两次
Tab
键即可。
- 变量(Variable)。如果你输入了美元符号($), shell 会给你补全可能的变量。
- 用户名(Username)。如果你输入了波浪符号(~), shell 会给你补全可能的用户名。
~username
表示该用户的主目录。
- 主机名(Hostname)。如果你输入了邮箱符号(@), shell 会给你补全/etc/hosts文件里的主机名。
命令连接与扩展
前面 Unix 哲学讲“ 程序,应当能够协作” 。命令协作靠的是元字符(metacharacter)功能。元字符有如下7个:|
,&
,;
,(
,)
,<
,>
。
匿名管道
参考网页:
管道字符|
,意如其名,类似管道一样将管道入口的数据通过管道传递给管道出口。
管道是为了解决进程间通信问题而存在,它可以让两个进程之间的数据进行传递,将一个进程的输出数据传递给另一个进程作为其输入数据。管道左边是数据给予方,管道右边是数据接收方。
管道仅能处理经由前面一个指令传出的正确输出信息,也就是 standard output 的信息,对于stdandard error 信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入 standard input。如下图所示,
- 管道命令只处理前一个命令正确输出,不处理错误输出
- 管道命令右边命令,必须能够接收标准输入流命令才行。
$ ps aux | grep "ssh"
litianci 11012 0.0 0.0 14660 1124 pts/0 S+ 15:39 0:00 grep ssh
按一般想法,先执行了ps
,得到输出后将输出数据传递给grep
,这时候grep
还没运行而ps
已经运行完毕了,为什么还能统计到grep
进程的信息呢?原因是管道实现的是进程间通信,两个进程之间存在交叉,在运行ps
进程后开始收集进程信息,grep也已经开始并处于等待接收数据状态,当ps
收集到任何数据后都将输出放入内存由管道传递给grep
进行筛选。
管道其本质是数据传递,管道左边的输出数据放入内存,由管道右边的进程读取。假如内存不足以完全存放输出数据,则管道左边的进程将一直等待,直到管道右边取出内存中一部分的数据以让管道左边的进程继续输出,而管道右边的进程在管道左边的进程启动后也立刻启动了,但是它一直处于等待状态,等待接收管道传递来的数据。
也就是说,管道左右两边的进程运行几乎是没有先后顺序的。
上述管道是指匿名管道。使用匿名管道的过程中,可能已经发现管道两边的进程是同属一个进程组的,也就是说管道左方的数据只能传递给管道右方的进程,其他任何进程都没法读取此数据。但除了匿名管道,还有命名管道,命名管道是将一个进程的数据存储到一个管道文件(fifo)中,其他进程可以读取该管道文件来读取其中的数据,也就是说不再限制数据读取方。关于命名管道,请参阅Linux/Unix操作系统内核或编程类的书籍,一般都会有详细的介绍。
再来两个简单例子
$ cat /etc/passwd | sort | less
$ gunzip < /usr/share/man/man1/grep.1.gz | nroff -c -man | less
解释
- 第1行命令,
cat
命令显示文件/etc/passwd的内容,输出到sort
命令进行排序,再送给less
命令显示出来。
- 第2行命令,
gunzip
命令解压缩grep.1.gz文件,送给nroff
命令调整为适合阅读的格式,传递到less
命令显示出来。
重定向
参考网页:
最常见的标准输入(stdin)、标准输出(stdout)和标准错误输出(stderr)的文件描述符分别是0、1和2,其中0、1、2也可以认为是它们的数字代号。
三个特殊的文件
标准输入 |
/dev/stdin |
0 |
<或<< |
标准输出 |
/dev/stdout |
1 |
>或>> |
标准错误输出 |
/dev/stderr |
2 |
2>或2>> |
<
、>
、2>
实现的是覆盖功能,>>
、2>>
实现的是追加的功能,但是<<
不是追加功能,而是表示此处生成文档(here document),在后面cat和重定向配合的内容里有说明。此外,还有<<<
,它表示此处字符串(here string),也见下文。
一般-
都是表示旧工作文件夹;有时候,-
也表示/dev/stdin,。如:
$ cat /etc/fstab | cat -
脚本中常见2>&1
和&>
的符号,它们都表示将stdout和stderr都重定向到同一个地方去,即重定向所有输出内容。如最常见的&> /dev/null
表示将stdout或stderr丢到/dev/null表示丢弃输出信息,反过来,将/dev/null重定向到某个文件则表示清空文件。
$ cat /dev/null > ab.sh
除此,还有以下几种方法快速清空文件
$ > ab.sh
$ : > ab.sh # 或"true >ab.sh",其实它们都等价于">ab.sh"
$ echo '' > ab.sh
$ truncate -s 0 ab.sh # truncate命令用于收缩和扩展文件大小
$ dd if=/dev/null of=ab.sh
最后最重要的一点:在有重定向符号的语句中,命令执行之前已经将文件截断了。所以如果正在编辑一个文件并将编辑的结果重定向回这个文件将出现异常,因为截断后就没有合适的内容用于编辑。一个简单的示例如下:
$ head a.log > a.log
有些时候直接使用>
覆盖输出是比较危险的。可以使用set -C
来设置如果输出重定向文件已经存在则不覆盖。使用set +C
来取消set -C
的效果。如果在设置了set -C
时仍然想强制覆盖,可以使用>|
代替>
来重定向输出。同理错误输出也有此特性。
$ set -C
$ cat flip >ttt.txt
-bash: ttt.txt: cannot overwrite existing file
$ cat flip >| ttt.txt
$ set +C
接下来讲 <<
一篇字符串。类似 PHP 语言的 <<<
功能
$ cat >log1.txt <<EOF
> this is stdin character first!
> EOF
解释
- 第1行,
<<EOF
表示下面另起一行,开始输入一篇字符,这篇字符直到其中一行为EOF截止。其中EOF可以换成你喜欢的字符串,一般采用大写,最好不要弄出特殊字符来。
- 第2行,就是输入的那篇字符。后面还可以输入很多。
- 第3行,输入EOF,表示这篇字符结束。
在bash中,<<
和<<<
是特殊重定向符号。<<<
表示的是其后字符串作为输入数据。
$ cat <<< PATH
等价于
$ echo PATH | cat
一般情况下,重定向要么将信息输入到文件中,要么输出到屏幕上,但是既想输出到屏幕又想输出到文件就比较麻烦。使用tee的双重定向功能可以实现该想法。
tee [-a] file
选项说明:
- -a:默认是将输出覆盖到文件中,使用该选项将变为追加行为。
- file:除了输出到标准输出中,还将输出到file中。如果file为“-”,则表示再输入一次到标准输出中。
例如下面的代码,将a开头的文件内容全部保存到b.log,同时把副本交给后面的的cat,使用这个cat又将内容保存到了x.log。其中“-”代表前面的stdin。
$ cat a* | tee b.log | cat - >x.log
还可以直接输出到屏幕:
$ cat a* | tee b.log | cat
tee默认会使用覆盖的方式保存到文件,可以使用-a选项来追加到文件。如:
$ cat a* | tee -a b.log | cat
重定向和管道的区别
管道命令与重定向区别
左边命令应该做到标准输出,右边命令应该接受标准输入 |
左边的命令应该有标准输出(输入),右边只能是文件 |
管道触发两个子进程执行| 两边的程序 |
在一个进程内执行 |
$ cat test.sh| grep -n 'echo' #“|”管道两边都必须是shell命令
$ grep -n 'echo' <test.sh #“重定向”符号,右边只能是文件
下面两个也是等同的。
$ (sed -n '1,$p'|grep -n 'echo')<test.sh
这个脚本比较有意思了。由于前面是管道,后面需要把test.sh内容重定向到 sed ,然后sed输出通过管道,输入给grep.需要将前面用“()”运算符括起来。在单括号内的命令,可以把它们看作一个象一个命令样。如果不加括号test.sh就是grep 的输入了。
$ sed -n '1,$p'<test.sh | grep -n 'echo'
重定向运算符,在shell命令解析前,首先检查的(一个命令,执行前一定检查好它的输入,输出,也就是0,1,2 设备是否准备好),所以优先级会最高
$ sed -n '1,10p'<test.sh | grep -n 'echo' <testsh.sh
哈哈,这个grep又接受管道输入,又有testsh.sh输入,那是不是2个都接收呢。刚才说了“<”运算符会优先,管道还没有发送数据前,grep绑定了testsh.sh输入,这样sed命令输出就被抛弃了。这里一定要小心使用
下面是输出重定向例子。
$ cat test.sh>test.txt
$ cat test.sh|tee test.txt &>/dev/null
通过管道实现将结果存入文件,还需要借助命令tee,它会把管道过来标准输入写入文件test.txt ,然后将标准输入复制到标准输出(stdout),所以重定向到/dev/null 不显示输出。“>”输出重定向,往往在命令最右边,接收左边命令的,输出结果,重定向到指定文件。也可以用到命令中间。
$ ls test.sh test1.sh testsh.sh 2>err.txt | grep 'test'
目录下面有:test,testsh文件,test1.sh不存在,因此将ls 命令错误输出输入到err.txt 正确输出,还会通过管道发送到grep命令。
$ ls test.sh test1.sh testsh.sh &>err.txt | grep 'test'
这次打印结果是空,&代表正确与错误输出 都输入给err.txt,通过管道继续往下面传递数据为空,所以没有什么显示的。
同样“>”输出重定向符,优先级也是先解析,当一个命令有这个字符,它就会与左边命令标准输出绑定。准备好了这些,就等待命令执行输出数据,它就开始接收
从上面例子可以看,重定向与管道在使用时候很多时候可以通用,其实,在shell里面,经常是【条条大路通罗马】的。一般如果是命令间传递参数,还是管道的好,如果处理输出结果需要重定向到文件,还是用重定向输出比较好。
顺序执行
顺序执行,类似C语言的;
语法。逐条执行命令。
$ date ; cat <<< LONGLONGJOB | cat - ; date
2018年 09月 29日 星期六 17:16:12 CST
LONGLONGJOB
2018年 09月 29日 星期六 17:16:12 CST
解释
- 本命令实现了对程序耗时的计算。而分号
;
的优先级是低于管道的。最终送给cat -
中cat
的只有LONGLONGJOB
。
后台命令
有些命令比较耗时,或者其他原因,我们希望他们运行在后台。就可以用如下方式。
$ tar cf test.tar test &
[1] 9355
$
[1]+ 已完成 tar cf test.tar test
解释
- 第1行,命令末尾加
&
即可让程序运行于后台。shell 会告诉该命令的进程号,示例中为9355
;并且会为他编号。
- 第4行,命令运行介绍,shell 会告诉你已经完成了该后台命令。
- 在运行过程中,如果你不想让他终止,请不要关闭该 shell。
命令扩展
把命令输出结果作为另外一条命令的参数,也是常用的一项功能,可以用美元符号$
和斜点(键盘左上角~键)`
,$(command)
和 `command`
。
$ vim -p $(find ~ | grep deepin-bible.tex)
解释
find ~
查找主目录所有文件,grep deepin-bible.tex
从前者找到文件名包含deepin-bible.tex
的所有文件,vim 把找到的所有文件打开。
简单数值计算
使用$[math expression]
的方式计算。
$ echo "我今年$[`date +%Y` - 1987]岁了。"
我今年31岁了。
解释
`date +%Y`
输出今年年数2018,$[2018 - 1987]
输出岁数。
输出变量值
一些变量的数值,可以通过$Var
输出。
$ echo $PATH
Shell 变量(Variables)
参考网页:
变量分为全局变量(环境变量)和局部变量(本地变量) 。环境变量可以在定义它们的shell及其派生出来的任意子进程的shell中使用。局部变量只能在定义它们的函数/脚本中使用。还有一些变量是用户创建的,其他的则是专用的shell变量,比如系统变量$0
等。
全局变量(环境变量)
环境变量可用于定义shell的运行环境,环境变量可以在配置文件中定义与修改,也可以在命令行中设置,但是命令行中的修改操作在终端重启时就会丢失,因此最好在配置文件中修改(用户家目录的“.bash_profile“文件或者全局配置“/etc/profile”、“/etc/bashrc”文件或者“/etc/profile.d”文件中定义。)将环境变量放在profile文件中,每次用户登录时这些变量值将被初始化。比如HOME、USER、SHELL、UID等再用户登录之前就已经被/bin/login程序设置好了。
摘自《Linux Bible》
常见环境变量表
BASH |
bash 命令的全路径地址,通常为 /bin/bash。 |
BASH_VERSION |
bash 版本号。 |
EUID |
这是当前用户的有效用户ID号,Shell启动时分配,基于用户在 /etc/passwd 的分配。 |
FCEDIT |
如果设置该变量,表示使用fc 命令来编辑命令记录(history commands);否则改用vi 命令。 |
HISTFILE |
命令记录文件,通常为 $HOME/.bash_history。 |
HISTFILESIZE |
命令记录条数,当超过此数,循环记录,最久的记录被删除。默认值1000。 |
HISTCMD |
返回当前命令处于命令记录的序号。 |
HOME |
当前用户的主目录,也就是你登录后,或者输入cd 回车跳到的文件夹。常用符号~ 表示主目录。 |
HOSTTYPE |
当前计算机硬件体系,比如 Intel 兼容机,一般是 i386, i486, i586, i686, 或者 i386 等数值。对于AMD 64位机,一般是 x86_64。 |
MAIL |
邮箱文件。深度普通用户一般都没有设置该变量,root用户一般为/var/mail/root。对于centos 系列一般为 /var/spool/mail/ |
OLDPWD |
前一个工作目录。当你频繁在两个目录切换的时候,当前目录的前一个目录就存储在该变量。通常使用减号- 表示。使用cd - 可以跳转到前一个目录。 |
OSTYPE |
本机操作系统类型。比如linux 或linux-gnu |
PATH |
路径变量。一般为一长串字符,用英文冒号: 拼接多个文件夹。Windows 系统也有该变量,只不过使用英文分号; 拼接的。处于上述文件夹下的可执行文件,只需输入本文件名不用输入完整路径,即可被shell找到执行。shell是从前往后找的,重名可执行文件,需要注意执行的是不是自己的文件。不在PATH 目录集合下的可执行文件,则需要输入绝对路径或者相对路径才可以执行。例如本机的root用户,PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 。 |
PPID |
当前shell父进程ID号。 |
PROMPT_COMMAND |
深度操作系统没有设置该变量。通常该变量为一条命令名称。在命令提示符(shell prompt)每次出现之前执行。 |
PS1 |
设置命令提示符。有时还会有PS2 , PS3 等。 |
PWD |
当前工作目录。 |
RANDOM |
返回 0 到 99999 的一个随机数。 |
SECONDS |
返回当前shell启用了多少秒数。 |
SHLVL |
这是与当前shell会话相关联的shell级别的数目。当您登录到shell时,SHLVL 为1。每次你新开 bash,比如使用su 或者键入bash ,该数字都会加一。 |
TMOUT |
深度操作系统没有设置该变量。设置一定秒数,如果 shell 在设定时间内没有输入,则退出。对于服务器来说,还是跟安全很相关的一个变量。 |
可以用echo
来显示查看全局变量(eg:echo $HOME
)。env
(或printenv
)、set
也可以用来查看系统的环境变量,但不是单独查看。而用unset
临时取消环境变量(eg:unset USER
),要永久生效还是要写到配置文件中
自定义环境变量(采用export):
- export 变量名=value
- 变量名=value;export 变量名
- declare -x 变量名=value
这里依旧是临时生效,在shell终端关闭后就消失了,写到配置文件中则永久生效(注意:写到配置文件中后需要运行一遍配置文件的脚本才可生效,否则等重启时生效)
命令行的三种方式测试如下:
关于环境变量PATH与export的更详细的内容,可参考: Linux环境变量与系统编程学习笔记
2、局部变量(本地变量):
本地变量在用户当前的shell生存期的脚本中使用。在一个函数中将某个变量声明为local,则该变量就是一个局部变量,只在本函数中有效。
定义:
- 变量名=value
- 变量名=’value’
- 变量名=”value”
shell中变量名的要求:一般遵循字母、数字、下滑线组成,不能以数字开头
别名的创建和使用
为了方便,常会配置一些别名。
$ alias l='ls -CF'
$ alias la='ls -A'
$ alias rm='rm -i
$ alias
$ \ls
$ unalias la
$ alias
解释
- 第1-3行,定义了三个别名。这些别名相当于后面的那个命令。
- 第4行,查看当前所有别名。
- 第5行,使用
\command
表示,使用最原始的命令,非别名。
- 第6-7行,暂时取消别名
la
,然后查看当前所有的别名。这个unalias
只可以在当前shell取消别名。
退出 shell
可以使用快捷键Ctrl+D
,或者输入exit
,或者直接点击窗口的关闭按钮。
定制 shell 环境
bash 的配置文件,一般如下表所示,
Bash 配置文件说明
/etc/profile |
影响所有用户,首次登录时执行。通常在此配置PATH ,MAIL ,HISTFILESIZE 等。最后,该文件会读取/etc/profile.d 文件夹下其他 shell 配置文件。 |
/etc/bashrc |
影响所有 bash 用户,每次打开 bash 都会执行。通常在此配置提示(prompt) PS1 和一些别名。变量设置会被用户自己的 ~ /.bashrc覆盖。 |
~/.bash_profile |
只影响该用户,首次登录时执行。一般存放环境变量,并会调用~ /.bashrc文件,特别适合放置环境变量。 |
~/.bashrc |
只影响该用户,每次打开 bash 都会执行。存放一些自己的配置,特别适合放置别名。 |
~/.bash_logout |
只影响该用户,每次注销(退出最后一个shell时)执行。通常只有清屏功能。 |
一般,修改/etc/profile,/etc/bashrc需要root权限,而且影响所有用户。后面三个文件,只影响本用户。
编辑好这些文件,需要执行才可以生效。
$ gedit $HOME/.bashrc #编辑文件
$ source $HOME/.bashrc #执行生效
配置提示(prompt)
参考网页:
Linux系统终端提示符,就是在前面8.2提到的$
或者#
之前的文字。是通过环境变量PS1
,PS2
,PS3
,PS4
配置的。一般都是配置系统环境变量PS1
(是“ prompt string one” 的简写)定义。通过命令echo $PS1
查看当前设置。
$ echo $PS1
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]$\setminus\$$
1、基本转义字符
PS1的值由一系列静态文本或\
和转义字符序列组成。示例,
$ PS1="\u@\H \w$ "
Shell 提示符中用到的转义字符
\a |
以 ASCII 格式编码的铃声。当遇到这个转义序列时,计算机会发出嗡嗡的响声。 |
\d |
以日,月,天格式来表示当前日期。例如,“Mon May 26” |
\h |
本地机的主机名,但不带末尾的域名。 |
\H |
完整的主机名。 |
\j |
运行在当前 shell 会话中的工作数。 |
\l |
当前终端设备名。 |
\n |
一个换行符。 |
\r |
一个回车符。 |
\s |
shell 程序名。 |
\t |
以24小时制,hours:minutes:seconds 的格式表示当前时间。 |
\T |
以12小时制表示当前时间。 |
\@ |
以12小时制,AM/PM 格式来表示当前时间,例如“10:51 PM”。 |
\A |
以24小时制,hours:minutes 格式表示当前时间。 |
\u |
当前用户名。 |
\v |
shell 程序的版本号,例如4.3。 |
\V |
shell 程序的版本号,例如4.3.11。 |
\w |
当前工作目录名。 |
\W |
当前工作目录名的最后部分。 |
\! |
当前命令的历史号。 |
\# |
当前 shell 会话中的命令数。 |
\$ |
这会显示一个$ 字符,除非你拥有超级用户权限。在那种情况下, 它会显示一个# 字符。 |
\[ |
标志着一系列一个或多个非打印字符的开始。这被用来嵌入非打印的控制字符,这些字符以某种方式来操作终端仿真器,比方说移动光标或者是更改文本颜色。 |
\] |
标志着非打印字符序列结束。 |
2、字体颜色
上面能够满足我们的效果了,但是相对于LinuxMint原始的提示符,缺少了颜色,不太美观。下面我们来学习如何添加颜色。大多数终端仿真器程序支持一定的非打印字符序列来控制,比方说字符属性(像颜色,黑体和可怕的闪烁) 和光标位置。
字体颜色是由一个ANSI 转义编码来控制的。该控制编码会嵌入字符流中并发送给终端仿真器。但是,该控制编码不会被“打印”到屏幕上,而是会被终端解释为一个指令。正如我们在上表看到的字符序列,这个\[
和\]
序列被用来封装这些非打印字符。一个 ANSI 转义编码以一个八进制033(这个编码是由退出按键产生的)开头,其后跟着一个可选的字符属性(0:正常、1:黑体、4:下划线、5:闪烁、7:反向(前景色和背景色反转)),在之后是一个指令。
用转义序列来设置文本颜色
\033[0;30m |
黑色 |
\033[1;30m |
深灰色 |
\033[0;31m |
红色 |
\033[1;31m |
浅红色 |
\033[0;32m |
绿色 |
\033[1;32m |
浅绿色 |
\033[0;33m |
棕色 |
\033[1;33m |
黄色 |
\033[0;34m |
蓝色 |
\033[1;34m |
浅蓝色 |
\033[0;35m |
粉红 |
\033[1;35m |
浅粉色 |
\033[0;36m |
青色 |
\033[1;36m |
浅青色 |
\033[0;37m |
浅灰色 |
\033[1;37m |
白色 |
例如我们来设置一个同LinuxMint默认的绿色提示符,
$ PS1="\[\033[01;32m\]\u@\W$\setminus\$$\[\033[00m\] "
于是,我们便有了下面的效果。
上面跟设置的提示符格式中的最后那个”\[\033[00m\]”
是用于将后续的字符颜色还原回原来的颜色。如果没有没有加最后这个转义码,则会出现下面结果(我们自己手工输入的命令也都将是绿色的)。
3、背景颜色
除了字体颜色,我们也可以设置字体的背景颜色。同样是通过转义的控制编码来实现,下表是背景颜色的控制编码。
用转义序列来设置背景颜色
\033[0;40m |
黑色 |
\033[1;44m |
蓝色 |
\033[0;41m |
红色 |
\033[1;45m |
粉红 |
\033[0;42m |
绿色 |
\033[1;46m |
青色 |
\033[0;43m |
棕色 |
\033[1;47m |
浅灰色 |
4、移动光标
转义编码也可以用来定位光标。这些编码通常被用来,每次当提示符出现的时候,会在屏幕的不同位置,比如说上面一个角落,显示一个时钟或者其它一些信息。下表是一系列用来定位光标的转义编码:
光标移动转义序列
\033[l;cH |
把光标移到第 l 行,第 c 列。 |
\033[nA |
把光标向上移动 n 行。 |
\033[nB |
把光标向下移动 n 行。 |
\033[nC |
把光标向前移动 n 个字符。 |
\033[nD |
把光标向后移动 n 个字符。 |
\033[2J |
清空屏幕,把光标移到左上角(第零行,第零列)。 |
\033[K |
清空从光标位置到当前行末的内容。 |
\033[s |
存储当前光标位置。 |
\033[u |
唤醒之前存储的光标位置。 |
有兴趣的可以试着配置一下,建议先备份,在配置到上面提到的配置文件里。
总结
本章整理了 shell 的一些基础知识,后面需要继续修改。