Linux深入探索04-Bash shell

----- 最近更新【2021-12-30】-----

本文目录结构预览:

  • 一、简介
  • 二、shell 变量
    1、查看变量
    2、变量类型
    3、变量操作
    4、系统常见的全局变量
  • 三、shell 选项
    1、查看 shell 选项
    2、设置 shell 选项
  • 四、元字符
    1、元字符列表
    2、引用与转义
  • 五、shell 内置命令
    1、查看说明
    2、常用内置命令
  • 六、搜索路径
    1、查看搜索路径
    2、修改搜索路径
  • 七、历史列表
    1、查看历史列表
    2、调取历史命令
    3、调取并修改历史命令
    4、搜索历史命令
    5、设置历史列表大小
  • 八、别名 alias
    1、语法
    2、创建别名
    3、查看别名
    4、移除别名
    5、不使用别名
  • 九、初始化文件
    1、文件名称
    2、登录 shell 与 非登录 shell
    3、初始化文件内容
  • 十、参考

一、简介

简单地来说,shell 就是一个 Unix 程序,充当用户界面和脚本解释器,允许用户输入命令以及间接地访问内核的服务。

从功能方面来说:
第一,shell 是一个读取并解释所输入命令的程序。用户每输入一条 Unix 命令,shell 就读取该命令,并指出应该怎么做,所以 shell 是一个命令处理器
第二,shell 还支持一些类型的编程语言。使用该语言可以编写由 shell 解释的程序,这些程序称为 shell 脚本

目前比较流行的shell有以下几种 :Bash、Korn shell、C-Shell、Tcsh。

Bash是目前最流行的 shell,本文也是以 Bash 为环境。

如果不知道你目前使用的是哪种 shell,可以使用命令echo $SHELL来查看。

本文内容可能会比较多,为了方面提前知道有哪些内容,这里先作一个简要的列表:

  • shell 变量
    --查看变量、变量类型、变量操作、系统常见的全局变量
  • shell 选项
    --查看 shell 选项、设置 shell 选项
  • 元字符
    --元字符列表、引用与转义
  • shell 内置命令
    --查看说明、常用内置命令
  • 搜索路径
    --查看搜索路径、修改搜索路径
  • 历史列表
    --查看历史列表、调取历史命令、调取并修改历史命令、搜索历史命令、设置历史列表大小
  • 别名 alias
    --语法、创建别名、查看别名、移除别名
  • 初始化文件
    --文件名称、登录 shell 与 非登录 shell、初始化文件内容

二、shell 变量

1、查看变量

上面说的$SHELL是 shell 的一个全局变量,shell 中还有很多其它的变量。可以使用命令env或者printenv去查看全局变量。

使用不带选项或者参数的 set 命令也可以显示所有的 shell 变量以及它们的值。

2、变量类型

根据储存类型,shell变量几乎总是存储一种类型的数据,即字符串。

根据变量的作用域(Scope),shell 变量可以划分为以下三种类型:

  • 有的变量可以在当前shell进程及其子进程中使用,这叫做全局变量
  • 有的变量仅可以在当前shell进程中使用,这叫做环境变量
  • 有的变量只能在函数内部使用,这叫做局部变量(作用域仅为某代码片断(函数上下文))

如果使用export命令将环境变量导出,那么它就在所有的子进程中也可以使用了,这时称为“全局变量”。

很多书本或网站对环境变量与全局变量的定义都比较模糊,我这里采用一种比较好理解的方式来定义。

3、变量操作

变量通常有4种不同类型的操作,即创建变量、查看变量、修改变量、删除变量。

变量的创建非常简单,使用如下语法就行:

变量名称=变量值  # 注意!等号前后不能有空格。

删除变量:

unset 变量名 ...

例:

[14:59 linux1@noseeu ~]$ MYNAME=Nosee    #创建变量(当前shell可用)
[14:59 linux1@noseeu ~]$ echo $MYNAME    #查看变量
Nosee
[14:59 linux1@noseeu ~]$ export $MYNAME     #导出变量(当前shell及子进程可用)
[15:00 linux1@noseeu ~]$ unset MYNAME    #删除变量
[15:00 linux1@noseeu ~]$ echo $MYNAME

[15:00 linux1@noseeu ~]$ 

注意:
通过 export 导出的变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么这个全局变量也就随之消失了,其它的进程也就无法使用了,这个变量只是临时的。
只有将变量写入 Shell 配置文件中才能达到永久的目的,Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。

4、系统常见的全局变量

HISTFILE # 历史列表:用来存储历史命令的文件名称
HISTSIZE # 历史列表:用来存储历史命令的最大数目
HOME # home目录
HOSTNAME # 计算机名称
LOGNAME # 当前用户标志(用户名)
PATH # 程序搜索目录
PS1 # shell提示
PWD # 当前工作目录
SHELL # shell类型
TERM # 终端类型
USER # 当前用户标志(用户名)

三、shell 选项

在Bash shell中,当我们需要控制 shell 行为的各个方面时,则可以使用shell 选项。

shell 选项就像 on/off 开关一样。当打开一个选项时,就说设置了这个选项;当关闭这个选项时,就说复位了这个选项。

shell 选项或者是 off 或者是 on ,它们不需要创建。

1、查看 shell 选项

可以使用命令set -o或者set +o来查看所有 shell 选项,两种查看方式只是在显示格式上不一样。

set -o命令以一种易于阅读的方式显示(人类可读)
set +o命令显示的输出适合用作 shell 脚本的数据(机器可读)

选项介绍:

选项 短名称 含义
allexport -a 导出随后定义的所有变量和函数
braceexpand -B 启用括号扩展(生成字符模式)
emacs 命令行编辑器:Emacs模式,关闭vi模式
hashall -h 查找到命令时(记住)的命令哈希位置
hisexpand -H 历史列表:启用!风格替换
history 历史列表:启用
ignoreeof 忽略eof信号^D;使用exit或logout退出shell
minitor -m 作业控制:启用
noclobber -C 不允许重定向的输出替换某个文件
notify -b 作业控制:当后台作业结束时立即通知
vi vi模式:立即处理每个键入的字符

使用命令 shopt可以查看更多的 shell 选项。

2、设置 shell 选项

语法:

set -o option  # 打开shell选项(开启)
set +o option  #复位shell选项(关闭)

注意,这里的-o表示开启一个选项,+o表示关闭一个选项。

1)如,我要开启某个选项(选项noclobber表示不允许重定向的输出替换某个文件)
则可以:

set -o noclobber # 开启该选项

或者:

set -C

关闭该选项则这样:set +o noclobberset +C

2)忽略eof信号^D

[21:34 linux1@noseeu ~]$ set -o ignoreeof
# 此时我再按 <Crlt>-D 组合键
[21:34 linux1@noseeu ~]$ Use "logout" to leave the shell.

设计 shell 的程序员们知道人们会如何使用 shell,因此在大多数情况下,默认的 shell 选项就可以满足要求。这意味着我们极少需要云修改 shell 选项。

四、元字符

在shell中,有许多字符拥有它特殊的含义,我们称这样的字符为元字符。如;(分号)、\(反斜杠)、.(点号),抽象一点的如按键<Space><Tab><Enter>也是使用了元字符。

1、元字符列表

字符 名称 作用
| 管道 命令行:创建一个管道线
< 小于 命令行:重定向输入
> 大于 命令行:重定向输出
() 圆括号 命令行:在子shell中运行命令
# hash、pound 命令行:注释
; 分号 命令行:用于分隔多条命令
` 反引号 命令行:命令替换
~ 波浪号 文件名扩展:插入home目录的名称
? 问号 文件名扩展:匹配任意一个字符
[] 方括号 文件名扩展:与一组字符中的字符匹配
* 星号 文件名扩展:匹配0个或多个字符
! 叹号、bang 历史列表:事件标记
& 和号 作业控制:在后台运行命令
\ 反斜杠 引用:下一个字符转义
' 单引号 引用:取消所有的替换
" 双引号 引用:取消大部分替换
{} 花括号 变量:确定变量名称的界限
$ 美元符号 变量:用变量的值替换
空格符 空白符:在命令行中分隔单词
制表符 空白符:在命令行中分隔单词
/ 新行字符 空白符:标记一行结束

以上元字符列表展示的就是元字符在shell中的常用作用。

2、引用与转义

有时候,我们希望按字面上的含义使用元字符(而不是使用其特殊含义),这时我们必须告诉shell按字面意思解释字符。这样做时,可以称其为引用字符。
字符的引用有3种方法:使用反斜杠、一对单引号或者一对双引号。

当使用反斜杠引用单个字符时,我们称反斜杠为“转义字符”。

如:

echo It is warm\; come on.

上面例子中,;(分号)在shell中有特殊的含义,如果我们想原样输出则必须转义。这时可以说“使用反斜杠转义了分号”,或者可以说“使用反斜杠引用了分号”。

当元字符少时,上面的方法没有什么问题。但是当元字符多的时候,我们就需要用到单引号或者双引号了。

1)强引用(单引号)
单引号里面的所有元字符都会被转义,也就是所有元字符都会原样输出。

如,下面的$会被转义,$NAME会原样输出:

linux1@noseeu:~$ echo 'My name is $NAME'
My name is $NAME

2)弱引用(双引号)
双引号引用时,会保留$(美元符号)、`(反引号)、\(反斜杠)的特殊含义。

如:

linux1@noseeu:~$ echo "My name is $NAME"
My name is Chan
[22:04 linux1@noseeu ~]$ echo "now is `date`."
now is Sun 26 Dec 2021 10:04:24 PM UTC.

上面的$不会被转义,$NAME会被替换为NAME的实际值;`也不会被转义,date会被当作嵌入命令优先执行。

注:反斜杠是所有引用中最强的,所以它甚至可以引用新行字符(<Enter>)。
如,

linux1@noseeu:~$ echo hi \
> nosee.
hi nosee.
linux1@noseeu:~$ date;\
> cal
Sun 26 Dec 2021 05:28:01 PM UTC
   December 2021      
Su Mo Tu We Th Fr Sa  
          1  2  3  4  
 5  6  7  8  9 10 11  
12 13 14 15 16 17 18  
19 20 21 22 23 24 25  
26 27 28 29 30 31 

在这里新行字符(<Enter>)失去了它的特殊含义,所以这时它并不是一行结束的信号了,意味着后面输入的内容都是接着上一行的。

五、shell 内置命令

当在shell中输入命令时,shell会将命令进行解析,然后决定如何处理命令,其中有两种可能。一些命令在shell的内部,这意味着shell可以直接解释它们,这些命令是内部命令,称为内置命令。其他所有命令都是外部命令,即必须独自运行的独立程序。

当输入内置命令时,shell在自己的进程内运行该命令(不创建新的进程)。当输入外部命令时,shell将搜索合适的程序然后以一个单独的进程运行该命令(创建一个子进程)。

查看某一条命令是不是内置命令的快捷方法是使用type。如:

linux1@noseeu:~$ type time set date type
time is a shell keyword
set is a shell builtin
date is hashed (/usr/bin/date)
type is a shell builtin

可以看出,time、set、type都是shell的内置命令,而date是外部命令。

1、查看说明

几乎所有的Unix程序在发行的时候都提供有说明明书页,即可以用man或者info去查看它们的说明。但内置命令不是一个单独的程序,它们是shell的一部分,每种shell都会提供大量的命令,所以每个内置命令都开发一个单独的说明书页是不现实的。

其实,所有的内置命令都记录在shell的说明书页中,即你可以使用man bash去查看。但是shell的说明书页都非常长,可能需要使用搜索才能找到所需的内容(可以使用apropos命令或者man -k命令进行搜索)。

还有一个方法,就是使用help命令也可以查看内置命令的说明,如:help unset

linux1@noseeu:~$ help unset
unset: unset [-f] [-v] [-n] [name ...]
    Unset values and attributes of shell variables and functions.
    ...
    ...
    Exit Status:
    Returns success unless an invalid option is given or a NAME is read-only.

使用不带参数的help命令,可以显示一个所有内置命令的摘要列表。如下图:

使用help -s命令可以查看某个内置命令的语法:

linux1@noseeu:~$ help -s set
set: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]

2、常用内置命令

alias # 为指定命令定义一个别名
echo # 将指定字符串输出到STDOUT
exit # 强制 shell 以指定的退出状态码退出
export  # 将变量导出到全局变量
fc  # 从历史记录中选择命令列表
help # 显示帮助说明
history # 显示命令历史记录
kill # 向指定的进程 ID(PID) 发送一个系统信号
pwd  # 显示当前工作目录的路径名
set # 设置并显示环境变量的值和 shell 属性
shopt # 打开/关闭控制 shell 可选行为的变量值
type # 显示指定的单词如果作为命令将会如何被解释
unset # 刪除指定的环境变量或 shell 属性

六、搜索路径

大部分命令都不是 shell 内置的,那么 shell 必须查找出合适的程序来执行。那么 shell 在哪里查找外部命令?

shell 通过查找变量 PATH 来获得一系列目录名称,然后在这些目录下寻找对应的程序,这些目录名称就是我们所说的搜索路径

1、查看搜索路径

搜索路径是包含所有外部命令的程序的目录列表,使用命令echo $PATH可以查看 搜索路径。

[21:31 linux1@noseeu ~]$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

搜索顺序:
当 shell 在查找外部程序时,它在搜索路径中会按指定顺序逐个检查目录,进到找到期望的外部命令时,它就停止搜索并执行程序。

2、修改搜索路径

修改搜索路径的基本思想就是将修改PATH变量的命令放到登录时自动执行的初始化文件中。

PATH的值就是一个包含若干目录名称的字符串,各个目录名称用:(冒号)隔开。

如我有一些自定义的程序放在 ~/bin 目录,那么我可以这样设置:

export PATH="$PATH:$HOME/bin"

一般修改 PATH 的值我们都是在原有的路径上再加上我们想要添加的路径。

七、历史列表

在输入命令时,shell 会将命令保存到所谓的历史列表中。然后我们可以采用不同方式访问历史列表,调取前面的命令,然后再对命令进行修改,并重新输入命令。

1、查看历史列表

最简单的查看历史列表的办法就是使用<Up><>Down键,但是这个方法每次只能查看一条命令。

还有一个更强大的命令,可以查看全部或者部分历史命令,就是使用history或者fc命令。

在历史列表中,每条命令称为一个事件,而每个事件都有一个内部编号,称为事件编号。历史列表的主要功能就是它可以基于事件编号调取命令。

查看历史列表:

[23:12 linux1@noseeu ~]$ fc -l
...
931      echo "'now is `date`."
932      help fc
933      fc-l
[23:12 linux1@noseeu ~]$ history
    1  sudo -i
    2  exit
...
934  fc -l
935  history

每条命令前面的数字就是事件编号。

2、调取历史命令

如我们想再次运行932号命令,可以使用fc -s 932或者!932

[23:14 linux1@noseeu ~]$ fc -s 932
help fc
fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
    Display or execute commands from the history list.
    ...
    Exit Status:
    Returns success or status of executed command; non-zero if an error occurs.
[23:22 linux1@noseeu ~]$ !932
help fc
fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
    Display or execute commands from the history list.
    ...
    Exit Status:
    Returns success or status of executed command; non-zero if an error occurs.

如果只是希望输入最近上一条命令,则可以不带事件编号:fc -s或者!!

3、调取并修改历史命令

shell 允许在重新执行历史命令之前对命令进行小的修改,语法如下:

fc -s pattern=replacement number
!number:s/pattern/replacement

例:

937      vim sh_test1 
[23:34 linux1@noseeu ~]$ fc -s sh_=Sh 937
vim Shtest1 
[23:35 linux1@noseeu ~]$ !937:s/sh_/Sh
vim Shtest1 

如果只是想修改上一条输错的命令,还可以这样:

[23:39 linux1@noseeu ~]$ datw
Command 'datw' not found.
[23:39 linux1@noseeu ~]$ ^w^e
date
Sun 26 Dec 2021 11:40:12 PM UTC
[23:40 linux1@noseeu ~]$ 

记住,这个方法仅适用于对上一条历史命令修改。

4、搜索历史命令

bash 还提供了一个非常方便的历史命令搜索方式,即使用<Ctrl>-R(^R)。
如我需要调取一条设置PS1变量的命令,查历史列表又觉得麻烦。这时我就可以按下<Ctrl>-R键,然后输入关键词PS:

linux1@noseeu:~$ 
(reverse-i-search)`PS': PS1="\[\033[0;32m\][\A \u\[\033[0;33m\]@\H \w]$ \[\033[0m\]"

如果显示出的命令是你想要的,直接按回车就可以执行了:

linux1@noseeu:~$ PS1="\[\033[0;32m\][\A \u\[\033[0;33m\]@\H \w]$ \[\033[0m\]"
[23:54 linux1@noseeu ~]$ 

如果看到的不是你想要的命令,可以继续按<Ctrl>-R搜索下一个。

5、设置历史列表大小

Bash shell 将历史列表存储在一个文件中,所以下次登录时还是可以继续使用。为了避免历史列表太多,shell 允许通过设置一个变量来设置历史列表的大小。

查看 shell 默认设置:

linux1@noseeu:~$ set | grep HIST
HISTCONTROL=ignoreboth
HISTFILE=/home/linux1/.bash_history
HISTFILESIZE=2000
HISTSIZE=1000

如我们要修改history命令所展示的历史列表条数:

linux1@noseeu:~$ export HISTSIZE=10

八、别名 alias

别名就是赋予一条命令或者一列命令的名称。可以将别名作为缩写,或者使用别名创建已有命令的自定义变体。

1、语法

Define or display aliases.

alias: alias [-p] [name[=value] ... ]

2、创建别名

我们经常会使用ls -al来查看文件列表,为了方便可以如下定义别名:

alias ll='ls -al'

当一条命令中包含有空格或元字符时,记得要使用引号包围。

linux1@noseeu:~$ alias mytime='date; cal'
linux1@noseeu:~$ mytime
Mon 27 Dec 2021 01:01:56 AM UTC
   December 2021      
Su Mo Tu We Th Fr Sa  
          1  2  3  4  
 5  6  7  8  9 10 11  
12 13 14 15 16 17 18  
19 20 21 22 23 24 25  
26 27 28 29 30 31  

3、查看别名

当需要查看某个别名的含义时,可以这样:

linux1@noseeu:~$ alias mytime
alias mytime='date; cal'

或者使用type命令:

linux1@noseeu:~$ type mytime
mytime is aliased to `date; cal'

查看所有别名:

linux1@noseeu:~$ alias
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -al'
alias ls='ls --color=auto'
alias mytime='date; cal'

4、移除别名

语法:Remove each NAME from the list of defined aliases.

unalias [-a] name [name ...]

例:

linux1@noseeu:~$ unalias mytime 
linux1@noseeu:~$ mytime
mytime: command not found

5、不使用别名

ls命令,很多 linux 会默认就在配置文件中设置了ls --color=auto,这时你就会在不知情的情况下使用了别名。如果你不想使用使用别名,可以在命令前面键入一个\,告诉 shell 不使用任何别名,如\ls

九、初始化文件

以上所说的所有设置都是只有当前shell生效,注销然后再登录之后那些设置都不存在了。如果你想在你下次登录时还可以继续使用你的设置,则需要把你的设置添加到初始化文件中。

Bash shell 的初始化文件包括两个,即登录文件环境文件

初始化文件存放着所有希望在每次登录时自动执行的命令,而环境文件存放着所有希望在新 shell 启动时自动执行的程序。

为了提供更多的功能,Bash shell 还提供了注销文件,用于存放注销登录时自动运行的命令。

1、文件名称

初始化文件的名称在不同的系统中可能会稍有不同,但基本都是以下这种:

执行环境 文件名称
登录文件 .bash_profile、.profile、.bash_login
环境文件 .bashrc
注销文件 .bash_logout

文件存放的目录都为用户目录,即:/home/用户名

注:
系统启动的时候会先执行.profile然后执行.bashrc,所以如果这两个文件存在相同的配置的话,前面的会被后面的覆盖。

2、登录 shell 与 非登录 shell

1)登录 shell
在任何时候,如通过ssh连接登录到远程主机,或者使用登录一个 shell,等。这类型需要用户进行登录认证才能进入的shell,我们称之为登录 shell

登录 shell 的初始化会执行登录文件(.profile)与环境文件(.bashrc)。

2)非登录 shell
当打开一个 shell 窗口不需要登录认证时,如在一个已登录的 shell 窗口输入 bash 打开一个新 shell,或者在桌面环境简单地打开一个终端窗口,等,我们称这类 shell 为 非登录 shell

非登录 shell 只执行环境文件。

3、初始化文件内容

shell 的初始化文件一般包含下述内容:
1)创建或者修改环境变量的命令
2)执行所有一次性操作的命令
3)合理的注释
4)等

如:

# 设置环境变量
HISTSIZE=50
HISTFILESIZE=1000

# 设置文件创建掩码,控制新创建文件的默认权限
umask 077

# 设置别名
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# 设置默认的分页程序(需要调用分页程序显示数据时就会调用该程序,如 man 命令)
export PAGER=less
# 设置分页程序的默认选项,相当于:alias less='less -CMs'
export LESS='-CMs'

# 查看命名为`.bashrc`的文件是否存在,如果存在,则运行这个文件
if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

十、参考

书箱:《Unix & Linux 大学教程》第11-14章 (美)Harley Hahn 著 张杰良 译
博客: 命令行界面 (CLI)、终端 (Terminal)、Shell、TTY的区别
SegmentFault: Linux TTY/PTS概述

posted @ 2022-01-25 12:23  四月不见  阅读(404)  评论(0编辑  收藏  举报