Bash/Shell

本文最后更新于:2024年7月24日 晚上

简介

Shell是运行在终端中的文本互动程序,是用户与Linux内核之间的接口程序,是二者沟通的桥梁[1][2]
Shell是一个命令语言解释器(command-language interpreter)。拥有自己内建的shell命令集。此外,Shell也能被系统中其他有效的Linux实用程序和应用程序(utilities and application programs)所调用。
不论何时你键入一个命令,它都被Linux shell所解释。一些命令,比如打印当前工作目录命令(pwd),是包含在Linux bash内部的(就象DOS的内部命令)。其他命令,比如拷贝命令(cp)和移动命令(rm),是存在于文件系统中某个目录下的单独的程序。而对用户来说,你不知道(或者可能不关心)一个命令是建立在Shell内部还是一个单独的程序[3]
图1 shell

Linux的Shell种类众多,常见的有:

  • Bourne Shell(/usr/bin/sh或/bin/sh,已经被 /bin/bash 所取代)
  • Bourne Again Shell(/bin/bash,就是 Linux 默认的 shell)
  • C Shell(/usr/bin/csh,已经被 /bin/tcsh 所取代,整合了 C Shell ,提供更多的功能)
  • K Shell(/usr/bin/ksh,Kornshell 由 AT&T Bell lab. 发展出来的,兼容于 bash)
  • Z Shell(基于 ksh 发展出来的,功能更强大的 shell)
  • Shell for Root(/sbin/sh)

Bash(GNU Bourne-Again Shell)是最常用的一种Shell,它是sh(Bourne Shell)的改进版,其中Bourne就是sh的作者。Bash是当前大多数Linux发行版的默认Shell,可以说,Bash之于Shell就如同Ubuntu之于Linux[4]
使用指令cat /etc/shells指令来查看当前Linux系统的可用Shell。(之所以会有这样一个文件来记录系统的可用Shell,是因为系统某些服务在运行过程中,会去检查用户能够使用的 shells ,而这些shell的查询就是藉由 /etc/shells 这个文件实现)
图2 查看可用shell

使用指令echo $SHELL查看当前系统所使用的Shell种类,比如我系统中使用的就是Bash。
图3 查看当前系统使用的shell

Bash能够作为所有Linux发行版的默认Shell,离不开它的以下几个优点[10]
- 命令记忆功能(history) 通过按下键盘的上下键就能够找到曾经输入的所有指令,相信曾为你提供不少的便利。在大多数Linux发行版中,能够记忆的指令高达100条,这些指令都被记录在家目录下的/bash_history中,不过此文件中记录的是上一次登录所使用过的指令; - 命令与文件名称补全(Tab按键的妙用) 这也是Bash Shell提供的非常棒的功能,为我们省去了背诵各种繁杂指令和文件名称的功夫,我们只需要记住命令开头几个字母按下Tab键就能自动补全名称或者显示所有可运行指令; - 别名配置(alias) 很多朋友喜欢使用的ll指令其实是ls -al的别名,可能他们还不自知(嘻嘻,想知道你的系统中使用了那些别名?在终端中键入alias试试看吧! - 工作控制、前景背景控制: (job control, foreground, background) 这个我不了解,详情请查看参考文献; - 程序化脚本: (shell scripts) 将你平时管理系统常需要下达的连续命令写成一个脚本文件, 该脚本可以通过对话交互式的方式来进行主机的侦测工作!整个设计下来几乎就是一个小型的程序语言; - 通配符: (Wildcard) 通配符可以帮助用户查询与命令下达,举例来说,想要知道/usr/bin底下有多少以 X 为开头的文件吗?使用:ls -l /usr/bin/X*就可以知道了。

Shell中执行命令的方式

Shell有两种执行命令的方式:
(1)交互式(Interactive):解释执行用户的命令,用户输入一条命令,Shell就解释执行一条。比如我们平时经常在terminal命令行使用cd、pwd、mv、cp、tar等命令,都属于此种方式。
(2)批处理(Batch):用户事先写一个Shell脚本(Script)如.sh文件,其中有很多条命令,让Shell一次把这些命令执行完,而不必一条一条地敲命令。
在使用交互式方法执行Shell命令时,通常有一些重要的快捷键需要我们掌握,掌握它们可以让我们执行命令时事半功倍。
图4 Terminal快捷键

Shell中命令的种类

Shell中命令有三类[5],分别是:
(1)内建(built-in)命令:是Shell程序的一部分,写在bash的源码builtins里面的,通常在Shell程序被加载驻留在系统内存中,解析内部命令不需要创建子进程,因此执行速度快于下面的外部命令,比如history、cd、exit[6],使用help即可列出所有内建命令,也可以使用help COMMAND查看内建函数的详细介绍;
图5 查看Shell所有内建命令

(2)外部命令:保存在shell之外的脚本,提供了额外的功能,是Linux实用程序的一部分,功能比较强大,不随系统一起被加载到内存中,外部命令虽然不在Shell中,但其命令的调用时由shell程序控制的,外部命令是在bash之后额外安装的,通常放在/bin,/usr/bin,/sbin,/usr/sbin等等。比如:lsvi等;
(3)别名(alias):给某个命令的简称。
图6 查看Shell命令种类

需要注意的是help命令不带任何参数的话只用于显示内建命令的帮助信息,包括一些简要说明以及一些参数的使用以及说明,如果加上--help的参数就可以查看外部命令的帮助信息了。
图7 使用help查看命令介绍

如果想查看更加详细的命令介绍,可以使用man,比help更加详细,而且无内建命令和外部命令之分,man好比一个电子词典,里面多是对命令的详细解释信息,help适合在紧急是忘记用哪个参数的时候用,不太紧急的适合可以用man[6]
> 比man更详细的帮助手册——info。

Shell编程[7]

在第二小节Shell中执行命令的方式中提到了批处理的方式执行Shell命令,这就涉及到Shell编程了,值得一提的是,业界所说的Shell通常都是指Shell脚本(script),但读者朋友要知道,Shell和Shell script是两个不同的概念。由于习惯的原因,简洁起见,本文出现的"shell编程"都是指Shell脚本编程,不是指开发Shell自身。
Shell脚本以.sh后缀结尾,使用vi/vimtouch创建test.sh文件,并使用vi/vim编辑,敲入一些代码,第一行一般为#!/bin/bash

1
2
#!/bin/bash
echo "This is test"

#!是一个约定的标记,告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程序。我们在第一小节简介 中提到shell又分很多种类,其中Bash(Bourne Again Shell)是最广泛使用的一种shell。在一般情况下,人们并不区分Bourne Shell和Bourne Again Shell,所以,像#!/bin/sh,它同样也可以改为#!/bin/bash
完成编写后,保存退出,并使用chmod 777 test.sh使其具有可执行权限,这样,我们的第一个Shell脚本就编写完成了。使用./test.sh就可以执行此脚本,可以看出会在terminal打印出“This is test”字符串。

Bash常见指令

  • alias: 设置bash别名;
  • bg: 使一个被挂起的进程在后台继续执行;
  • cd: 改变当前工作目录;
  • exit: 终止shell;
  • export: 使变量的值对当前shell的所有子进程都可见;
  • fc: 用来编辑历史命令列表里的命令;
  • fg: 使一个被挂起的进程在前台继续执行;
  • help: 显示bash内部命令的帮助信息;
  • kill: 终止某个进程;
  • pwd: 显示当前工作目录;
  • unalias: 删除已定义的别名。

Bash常见环境变量(在/etc/profile文件中配置)

这里是几个最有用的环境变量,包括变量名和简单描述:

  • EDITOR, FCEDIT: bash fc 命令的缺省编辑器;
  • HISTFILE: 用于贮存历史命令的文件;
  • HISTSIZE: 历史命令列表的大小,表明可以存储多少条历史指令,默认是1000条;
  • PWD: 当前工作目录;
  • PATH: 预设可执行文件或命令的搜索路径。env命令显示所有的环境变量 。环境变量以:分开。环境变量名前$符号表示该变量本次定义之- 前的值。由于文件的搜寻是依序由 PATH 的变量内的目录来查询,所以,目录的顺序也很重要;
  • USER: 用户登录时使用的用户名;
  • HOME:当前用户主目录;
  • LANG/LANGUGE:这个很重要,表示语系,很多信息都会用到他, 举例来说,当我们在启动某些 perl 的程序语言文件时,他会主动的去分析语系数据文件, 如果发现有他无法解析的编码语系,可能会产生错误!一般来说,我们中文编码通常是 zh_TW.Big5 或者是 zh_TW.UTF-8,这两个编码偏偏不容易被解译出来,所以有的时候,可能需要修订一下语系数据;
  • SHELL:是指当前用户用的是哪种Shell。set命令显示所有本地定义的Shell变量,包括环境变量和自定义变量,而前面提到的env指令只会显示环境变量;
  • LOGNAME:指当前用户的登录名。其值为$USER
  • HOSTNAME:所使用的主机名。供应用程序使用;
  • PS1:命令行的一级提示符(格数如下,用法如右):export PS1="[\u@\h \w]\$"[8]\d代表日期,格式为weekday month date,例如:Wed Dec 12;
    \H完整的主机名称。例如:hostname是debian.linux;
    \h仅取主机的第一个名字,如上例,则为debian,.linux则被省略;
    \t显示时间为24小时格式,如:HH:MM:SS;
    \T显示时间为12小时格式;
    \A显示时间为24小时格式:HH:MM;
    \u当前用户的账号名称 如:root;
    \vBASH的版本信息  如:3.2;
    \w完整的工作目录名称。家目录会以~代替 如显示/etc/default/
    \W利用basename取得工作目录名称,只会列出最后一个目录。如上例则只显示default;
    \#下达的第几个命令;
    \$提示字符,如果是root时,提示符为:#,普通用户则为:
  • PS2:命令行的二级提示符,默认为>[9]
  • MAIL: 存放用户电子邮件的邮箱(ASCII码文件);
  • INPUTRC: 存放的是针对键盘热键的信息(ASCII码文件);
  • $:代表目前这个 Shell 的线程代号,即所谓的 PID (Process ID)
  • ?:代表上一个运行的命令所回传的值

在这里对PS1这个变量进行进一步的解释,PS1这个变量就是用来定义每次使用终端时前面那一行的样式的,比如在这里定义的样式为yeyisheng@ubuntu:~$
图8 PS1作用的结果

注意,如果在shell中修改该变量的值,只是临时生效的,一旦注销或重启系统就会消失。要想永久生效,必须写入环境变量配置文件