1. 什么是shell?
shell是一种用户接口,我们可以通过shell来与计算机进行通信,或者说可以通过shell来操作计算机。所以任何可以操作计算机的程序应该都可以叫做shell。事实上,shell是一个循环执行的程序。它的框架可以大体表示如下:
while you do not exit or logout {
print a prompt
read a string
parse the string into commands and arguments
create a new process
in the child process:
execute the command you typed passing it the argument list
in the shell wait for that command to complete
}
可以用C库提供的一些函数实现一个简单的shell,如下:
/* shelldemo.c
* The demo emulates a shell which shows how a shell work.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define BUFSIZE 128
int main()
{
pid_t pid;
char buf[BUFSIZE];
while (1) {
memset(buf, '\0', BUFSIZE);
printf("> ");
scanf("%s", buf);
if (!strcmp(buf, "logout")) {
printf("Bye bye!\n");
break;
}
pid = fork();
if (pid == 0) { //child
execlp(buf, "", NULL);
}
if (pid > 0) { //parent
wait();
}
}
return 0;
}
它可以执行不带参数的命令,它符合上文提到的那个框架,当然真正的shell不是长这个样子的。
除了和用户交互,shell的强大功能在于它的可定制性、自动补全、记忆命令等各种各样的功能。
2. 使用Bash
Bash是shell的一种,也是大多数linux发行版默认的shell。我们可以直接与bash进行交互式的操作,也可以通过脚本文件来进行操作。可以使用“echo $SHELL”来查看当前shell的类型。bash本身就有很多参数:
- bash -i: 让bash以交互模式工作,这是bash默认的工作方式。
- bash -r: 让bash在受限模式下工作,这在嵌入式或者其他一些需要限制用户活动的系统中是很有帮助的,需要跟其他一些参数和配置结合使用才能达到需要的效果。这里给个例子看下-r参数的效果:
$ bash -r $ cd / bash: cd: restricted
- bash -c string: bash执行string,如果string后边还有参数,则在string中以$0, $1,……代替。如:
$ bash -c 'echo $0 - $1 = $2' three two one three - two = one
- bash -P: 这个和链接文件有关。比如有一个目录叫做test1,然后用ln -s做一个链接到test2。在test2中查看当前路径(pwd),会显示/path/to/test2;在bash -P环境中,查看当前路径,则显示/path/to/tes1。这在某些情况下可以避免混淆。
- bash -v: 显示bash的详细信息。
还有很多参数,可以查看bash的man page。
3. 配置文件
- ~/.bashrc
- ~/.profile (不同的发行版可能名字不太一样,Archlinux中为.bash_profile)
- /etc/profile
- 不同的发行版中可能还有一些其他的配置文件
3.1 默认配置
- /etc/profile: 这是一个系统级别的配置文件,可以影响到所有的用户。
- ~/.bashrc: 这是一个用户级别的配置文件,只对特定用户起作用。
3.2 login shell
login shell可以理解为提示你输入用户名密码的那个shell。用户名密码验证正确以后,shell会做一些初始化,包括读取shell的配置文件。之后打开的shell不会在重复初始化的工作。
3.3 非交互模式
使用shell脚本
4. 历史记录
能查看以前执行过的命令,是shell的特点和优势。这可以在你写文档的时候一些提供帮助,也可以帮助你快速执行很多曾经执行过的复杂的命令。这里先介绍一下环境变量。每个进程的运行环境,都是在shell执行相应的程序时,由shell设置好并传递给它的。export和unset用来设置和移除环境变量。
4.1 历史记录
使用history可以查看以前输入的命令。与历史记录的相关环境变量:
- HISTSIZE: 设置记录命令的条数,"export HISTSIZE=500"最多可记录500条命令。
- HISTFILESIZE: 事实上,所有的命令都是记录在~/.bash_history文件中的,在shell中可以记录HISTSIZE条命令,但是单退出shell的时候,写入.bash_history文件的命令会被截断成最新的HISTFILESIZE条。
- HISTFILE: 这个变量用来定义一个存储命令记录的文件,而不使用默认的.bash_history。
4.2 检查历史命令
- 清除历史记录,在你确定要清除之前,最好先备份一下命令的历史记录
$ history > history.bak $ history -c
- 查询历史记录。使用history命令时,显示出来的每条记录都有一个编号,变量HISTCMD记录的是当前指令的下一条指令的编号。这个大家可以通过“echo $HISTCMD”来查看。另外,bash提供了一个事件操作符“!”,通过“!”+命令记录编号,就可以执行那条指令。比如用history查处第514条指令为ls,则在命令行输入!514,就可以执行ls指令。
4.3 完整的历史记录
用history查询历史记录的时候存在这样一个问题:如果你编译一个东西,第一次使用/usr/bin/gcc,第二次使用交叉编译工具/opt/dev_kit/bin/gcc。在history中只会记录gcc,这样以后再看history记录时会产生误解。这是就需要hash命令。它会显示在当前会话中所有命令的完成路径,并且会记录使用次数。
$ hash
hits command
2 /usr/bin/vim
1 /opt/ELDK/usr/bin/make
1 /opt/ELDK/usr/bin/info
5 /bin/ls
4.4 搜索历史记录
使用快捷键<CTRL>+R,就可以搜索命令的历史记录。输入相关的字符,就会显示出相关的历史命令,再次使用<CTRL>+R,就可以查找相关的下一条历史命令。当然,这里也可以使用先前提到的事件操作符“!"来搜索最近使用过的包含某些字符的命令。用法为“!?字符?”,需要搜索的字符要放在两个文号之间,如
$ history
... ...
498 which echo
499 ls
500 history
$ !?l?
which echo
/bin/echo
5. 命令行上的编辑操作
学会一些快捷操作,会使你的命令行操作更加迅速。
5.1 光标
这里先给一些快捷键,大家可以一个一个试试看:
- <Ctrl + c>: 这个应该大家都知道 :-)
- <Ctrl> + f: 光标向后移一个字符
- <Ctrl> + b: 光标向前移一个字符
- <Ctrl + _> (需要按住Ctrl+Shift+-三个键): 撤销对命令行的修改
- <Ctrl + a>: 将光标移动到行首
- <Ctrl + e>: 将光标移动到行尾
- <Ctrl + d>: 删除当前光标处的一个字符
- <Ctrl + l>: 清屏,和clear命令一样
- <Ctrl + k>: 删除从当前光标位置到行末的所有字符
- <Ctrl + w>: 删除从当前光标位置到行首的所有字符
5.2 正则表达式
可以结合sed命令,来对命令做一些正则比配。
5.3 别名
可以用alias命令来定义一些别名操作,使一些很长的命令可以变得很短。比如在debian中,root用户ls出来的结果是没有颜色的。可以在/root/.bashrc中加入“alias ls='ls --color=auto'”来是ls的结果带上颜色,以便区分普通文件、目录、可执行文件等。也可以直接用alias命令来查看你的shell中设置了哪些别名。
5.4 配置环境变量
配置PATH。一般搭建交叉编译环境以后,都需要设置PATH这个环境变量。当你输入一个(不包含完整的)命令时,系统会自动搜索PATH环境中设定的路径,然后运行找到的第一个程序。设置PATH:
$ export PATH="$PATH:/path/to/executables"
5.5 自动补全
<Tab>键的补全或提示功能十分有效。在命令行连按两次<Tab>,会显示系统中的所有命令。