L o a d i n g . . .
SHIWIVI-文章

//sunny forever
while(life<end){
love++;
beAwesome :)}

    <
  • 主题:
  • + -
  • 清除背景
  • 禁用背景

Shell介绍与使用

字数:20863 写于:2021-12-29
最新更新:2024-06-02 阅读本文预计花费您60分钟

shell介绍

shell简介

shell意为层壳,区别于操作系统Kernel(内核),shell是一个命令行解释器,主要用来与用户交互,将用户的命令解释给操作系统,是用户与操作系统内核交互的桥梁,它提供文件操作、进程控制、环境变量设置、管道和重定向等各种功能,因此,Shell 也是许多脚本和自动化任务的基础。此外,shell还有一套自己的编程语法,用于编写shell脚本,shell脚本支持直接调用Linux系统命令

常见的类Unix操作系统中的Shell程序包括:

  • sh(Bourne Shell):是Unix系统上最早的shell,由Stephen Bourne于1977年在AT&T贝尔实验室开发。尽管现在有许多更现代的shell可以使用,但许多Unix系统命令和脚本仍然使用sh作为默认解释器
  • Bash(Bourne-Again SHell): bash是Bourne Shell的增强版,兼容sh的命令和脚本,并结合了ksh、csh的有用功能。作为GNU项目的一部分,它具有强大的功能和灵活性,支持命令历史、自动补全、脚本编程等功能,是许多Linux系统的默认Shell
  • Csh(C Shell):csh是基于C语言语法的shell,它提供了类似于C语言的语法结构和编程特性,由Bill Joy于1978年开发
  • Tcsh(TC Shell):Tcsh是C Shell的改进版本,提供了更多的功能和灵活性
  • Fish(Friendly Interactive Shell):Fish是一个用户友好的交互式shell软件,具有直观的语法和自动补全功能,还提供了颜色高亮和语法提示等功能,被广泛包含于如 Debian、Ubuntu、Fedora、Gentoo、Arch 等操作系统中
  • Ksh(Korn Shell):由贝尔实验室的David Korn在1983年基于Bourne shell的源码开发,它结合了Bourne Shell和C Shell的特性,并引入了许多新的功能
  • Zsh(Z Shell):由普林斯顿大学的Paul Falstad开发,它包含了 bash,ksh,tcsh 等其他shell中许多优秀功能,可以作为bash的替代品,它也是macOS 10.15及新版系统的默认shell

在这些众多的shell中,有一些shell软件有类似的语法和特性,由此形成了两大家族:Bourne Shell家族:通常为Linux的默认使用终端,包括sh、ksh、Bash、psh、zsh。以及C Shell家族:包括csh、tcsh,语法与C语言类似,主要用于BSD版Unix系统。

切换shell

查看/etc/shells文件可获取当前Linux支持的shell列表,绝大多数Linux发行版默认使用Bash作为命令解释器,直接输入其他shell名可进行切换,通过exit命令返回默认的Bash终端

父子shell

默认情况下,系统会在用户登录时根据/etc/passwd文件第7字段的设置启动用户的默认shell,这是一个父shell,如果此时输入bash或其他shell的启动命令,会创建一个新的shell程序,这个shell程序是一个子shell

创建子shell可以保持父子进程环境的纯洁性,创建子shell时,只有部分父进程环境会被复制到子shell中,因此子shell会在一个相对纯洁的环境中执行任务,但这也会造成父shell中的一些变量(如用户自定义的局部变量)无法被子shell中继承使用。在子shell中执行一些独立任务,或者修改一些环境也不会影响到父进程的执行。但是创建子shell成本不菲,它会显著占用一部分系统资源

以下操作会创建一个子shell:

  • 通过bash等命令显式调用shell解释器,会启动一个新的子shell
  • 通过bash filename.sh./filename.sh方式执行shell脚本时,会创建一个子shell执行该脚本。相对的,通过source filename.sh或.filename.sh方式执行脚本,会在当前shell中执行而不会创建子shell
  • 在命令中使用管道符|时,shell会创建子shell,管道两边的命令都会在单独的子shell中执行。
  • 使用反引号` `$()执行命令替换时,命令会在子shell中执行
  • 将命令放到( )中,所有括号中的命令会在子shell中执行,这是最直接创建子shell的方式,通常用来执行多个需要在同一个shell环境中执行的一组命令
  • 使用&将任务放到后台执行时,为了不阻塞当前shell执行其他任务,bash会在后台创建子shell执行命令
查看父子shell的创建关系

子shell也可以创建另一个子shell,产生嵌套,通过ps -f命令查看shell程序的PPID来查找其父进程

UID PID PPID C STIME TTY TIME CMD root 27576 27573 0 15:11 pts/0 00:00:00 -bash #父shell root 27602 27576 0 15:11 pts/0 00:00:00 bash #子shell,父进程PID为27576 root 27619 27602 0 15:11 pts/0 00:00:00 ps -f
也可以通过ps -‌-forest命令查看子shell的嵌套结构
PID TTY TIME CMD 27576 pts/0 00:00:00 bash 27602 pts/0 00:00:00 \_ bash 27923 pts/0 00:00:00 \_ bash 27939 pts/0 00:00:00 \_ bash 28000 pts/0 00:00:00 \_ ps

也可以通过输出环境变量BASH_SUBSHELL来查看当前bash有没有生成子shell,如果该命令返回0,则表明没有子shell,返回1或其他更大的值,就表明有相应数量的子shell。但该命令需要在父shell环境下执行,因此一般在命令分组并形成进程列表时使用

(pwd;ls;echo $BASH_SUBSHELL)

bash shell程序可使用命令行选项修改shell启动方式,以下为bash中可用的命令行参数

参数 说明
-c string 从string中读取命令并进行处理
-i 启动一个能够接收用户输入的交互shell
-l 以登录shell的形式启动
-r 启动一个受限shell,用户会被限制在默认目录中
-s 从标准输入中读取命令
父子shell的使用场景

如果命令或脚本需要在一个纯净的隔离环境下执行,或者希望执行某个操作但不改变当前的shell环境,创建子shell十分有用,但需要注意创建子shell带来的资源占用问题

1.前往tmp目录并创建一个新文件,但不切换当前目录 (cd /tmp && touch newfile) #不会切换当前父shell的工作目录 2. 临时更改环境变量,使子shell在某个新的环境变量下工作,但不影响父shell (export PATH=/custom/path && test.sh) 3. 通过( )创建多个子shell时,这些子shell会被依次创建,只有执行完第一个子shell中的任务,才会创建第二个 如:创建文件test.sh并写入以下命令 (sleep 3) # 3 seconds (sleep 2) # 2 seconds (sleep 1) # 1 second 使用time()函数计算脚本执行时间 time(bash test.sh) #大约需要6s 但通过&创建子shell,这些任务会被放入后台,子shell会并行创建并执行 如:将文件test.sh修改为 sleep 3 & # 3 seconds sleep 2 & # 2 seconds sleep 1 & # 1 second wait 然后计算执行时间 time(bash test.sh) #大约需要3s,取决于执行时间最长的子shell

内建命令与外部命令

外部命令

外部命令,又被称为文件系统命令,是存在于bash shell之外的程序。它们并不是shell程序的一部分。外部命令程序通常位于/bin、/usr/bin、/sbin或/usr/sbin中,可以使用whichtype -a命令找到命令位置。执行外部命令时,会创建一个该命令对应的子进程,相对于内建命令来说,外部命令需要花费时间和资源来设置新子进程的环境

# which ps /bin/ps

内建命令

内建命令不需要使用子进程来执行,它们已经和shell编译成了一体,在执行时速度更快,消耗的资源更少。常见的内建命令有cd、echo、pwd、history、alias、continue、exit、jobs、kill

可以使用type命令来查看某个命令是否为内建命令

# type cd cd is a shell builtin

有些命令有多种实现方式,如:echo和pwd既有内建命令又有外部命令,默认使用内建命令,如果需要使用外部命令,则指定命令的具体路径即可。可以用type -a命令查看命令的不同实现

# type -a echo echo is a shell builtin echo is /bin/echo

命令的执行优先级

  • 第一顺位执行通过绝对路径或相对路径执行的命令
  • 第二顺位执行自定义的命令别名
  • 第三顺位执行bash内部命令
  • 第四顺位执行$PATH环境变量定义的第一个命令

命令分组执行

可以在一行中指定多个命令,命令之间使用分号(;)分隔,称为命令列表,这些命令会依次执行

$ pwd ; ls ; cd /etc ; pwd ; ls

可以使用大括号或小括号将这些命令组合在一起,以便在特定情况(如在if-then语句中)一起执行,这称之为命令分组,主要有两种不同的命令分组:

  • 使用大括号{ }将多个命令组合在一起,可以在当前shell环境中执行,该方法也可用于shell脚本中if-then语句的结构控制

    { pwd ; cd /etc ; ls }
  • 如果将命令列表写在括号里,则shell会创建一个子shell执行这些命令,称为进程列表,这样可以使用多个子shell进行多进程处理

    创建1个子shell,输出值为1 (pwd ; ls ; cd /etc ; pwd ; ls ; echo $BASH_SUBSHELL) 括号可以嵌套,创建多个子shell进行多进程处理,创建了2个子shell(父shell创建子shell,子shell又创建了它的子shell),输出值为2 ( pwd ; (echo $BASH_SUBSHELL))

创建协程

shell中的协程用于在后台创建一个子shell,并在该shell中执行命令,其实就相当于使用&将命令置入后台模式,但其有点在于我们可以为该协程命名,并且该协程创建时会自动创建一个匿名管道,并将子shell的输入输出重定向到这个管道,实现在 Bash 脚本中并发地执行子进程,并与该子进程进行双向数据通信的功能

协程的创建需要使用命令coproc,其语法为:

coproc 协程名 { 命令1; 命令2; }

如果不指定协程名,则默认为COPROC,之后命令会被置入后台模式,返回后台作业号和进程ID

coproc my_process { echo "子进程开始"; sleep 2; echo "子进程执行完毕"; } # 读取子进程的输出 while read -u "${my_process[0]}" output; do echo "父进程获得子进程输出: $output" done wait # 等待后台进程结束 echo "父进程执行完毕"

Bash常用快捷键

GUN官方的Bash文档:Bash Reference Manual
快捷键 说明
ctrl+a 将光标跳转到命令行开头
ctrl+e 将光标跳转到命令行末尾
ctrl+左箭头 光标向左(前)跳转一个单词,esc+b也可以
ctrl+右箭头 光标向右(后)跳转一个单词,esc+f也可以
ctrl+u 剪切光标到行首之间的内容(也用来快速删除一整行命令)
ctrl+k 剪切光标到行尾之间的内容
ctrl+y 粘贴剪切的内容
clear 清屏
ctrl+l 清屏,同clear命令
exit 退出当前终端
ctrl+d 退出当前终端,同exit命令
ctrl+c 强制终止当前任务
ctrl+z 暂停当前任务并放入后台
ctrl+r 反向搜索执行过的历史命令
# root用户的命令提示符
$ 非root用户的命令提示符
> 次提示符
\ 延续字符
修改快捷键参考stty命令

Bash的内建命令

history历史命令

在bash中执行过的命令会被缓存在内存中,方便重复调用,当shell退出或用户登出时会被写到用户对应的.bash_history历史命令记录文件中

history [选项] [历史命令保存文件]查看使用过的命令,可以在后面加一个值指定显示n条命令

  • -c 清空历史命令
  • -w 把缓存的历史命令写入历史命令保存文件~/.bash_history中
  • -a 将缓存的历史命令追加到历史命令保存文件中
  • -d 值 删除该条历史命令记录
  • -s 字符串 将该字符串添加到当前会话的命令历史中,但不执行
每个用户都有独立的历史命令保存文件,一般在用户家目录下,该文件默认隐藏,用户执行过的命令会先缓存在内存中,等登出账户后再写入bash_history文件中,历史命令默认保存1000条,用户可在环境变量配置文件/etc/profile中的HISTSIZE选项自行修改
调用历史命令
  • 可通过上下箭头调用之前执行的命令
  • !!再次执行上一次执行的命令
  • !n再次执行第n条历史命令(序号以history列出的序号为准)
  • !字符串再次执行最后一次以该字符串开头的命令

alias自定义命令名

alias查看所有自定义的命令

alias 别名=’原命令’自定义命令名,原命令依旧可用

使用命令定义的命令名只能临时生效,且只在当前shell中生效,系统重启后将丢失,要永久生效需将自定义的命令添加到文件/用户名/.bashrc

删除自定义的命令:

unalias 别名

type查询命令类型

查看所指定的命令是shell 内建命令、外部可执行文件,还是其他类型

type [选项] 命令

  • -a 查看所有可能的命令类型,有的命令可能同时有shell内建、外部二进制文件、用户别名等多种类型,该选项会列出所有情况
  • -t 只显示命令类型,而不显示命令路径,返回类型可能有:lias(别名)、builtin(内建命令)、file(外部命令)、function(定义的函数)、keyword(shell保留的关键字)
  • -p (小写)只显示命令路径,如果命令是shell内建命令或别名,使用此选项不会有输出
  • -P (大写)在PATH中查找该命令,无论该命令是何种类型,都返回其路径,该选项常被用来在shell脚本中查询并精确执行某命令,如$(type -P curl)
  • -f 不在shell函数中查找,即不将命令视为函数来查找

exec命令

exec命令的主要功能有两个

替换当前shell进程
exec [选项] 执行命令
  • -c 清除所有环境变量(除了HOME、SHELL、PATH、TERM、MAIL、USER和LOGNAM)
  • -l 启动一个新shell执行对应的命令,该shell以登录shell的身份工作

当使用exec命令执行命令或可执行文件时,新执行的程序会替换当前的shell进程。exec命令会将指定的命令或程序加载到当前shell进程的内存空间中,并替换 shell 进程的执行上下文,包括内存代码段、数据段、堆栈中的数据等,同时,新进程会继承原进程的大部分环境和信息,包括进程PID、环境变量、当前工作目录、打开的文件描述符、用户和用户组ID、进程优先级、以及对进程的资源限制(如内存限制、文件大小限制)等信息,这样可以在不创建新进程的情况下,执行新的功能和程序,极大程度上节省了用于创建新进程消耗的系统资源,可以有效减少进程数量并简化进程管理。

该命令可用于在不创建新进程或子shell的情况下切换shell并替换当前的shell进程,通过exec命令切换,Linux会保持shell的PID、用户和用户组ID、当前工作目录等环境不变,也不会创建子shell消耗更多的系统资源

e.g.从bash切换到sh exec /bin/sh

该命令也可以用其他程序替换当前的shell进程,例如,在脚本的最后一行使用 exec 来启动一个长期运行的程序,当脚本即将运行结束,shell的使命也即将完成,通过exec命令让新的程序替换shell进程,而不再需要创建新的进程,可以有效节约系统资源

#!/bin/bash # 通过shell执行脚本功能 # 即将运行结束,让新进程替换shell进程 exec /path/to/long_running_application

此外,exec命令还被用于以下场景:

  • 当某个进程认为自己不能再为系统和用户作出任何贡献时,就可以调用exec命令让新的进程替代自己,如:一些守护进程,或用于系统初始化启动的进程,当系统完成启动,这些进程的使命已经完成,exec命令可以使新进程接管自己的初始化环境,减少进程间的通信和数据拷贝,提高系统性能
  • 在一些特定环境中,使用exec命令可以保证某个关键任务完全接管当前进程,而不会有多余的 shell 进程存在
  • 可以用于动态加载新程序和脚本,在其环境不变的情况下执行新脚本,而无需额外创建进程
  • 在某些需要严格控制权限的环境中,用 exec 替换当前进程可以确保新的进程继承当前进程的所有权限和环境,使进程在受限的环境中启动并继承了相应权限
注意!如果使用远程终端在登录shell中使用exec命令,如:执行 exec ls -l,ls命令会替换当前shell进程,这会导致远程shell会话终止,远程连接断开。因此如果要在远程连接时执行该命令,最好在子shell中使用,在登录shell中使用会导致ssh连接断开
修改文件描述符
exec 重定向命令

exec 命令还可以用来重定向文件描述符。在这种用法中,exec 并不会替换当前的 shell 进程,而是会替换脚本的执行环境,该命令可用来重定向整个脚本的标准输入输出文件,或者关闭不需要的文件描述符

e.g.将脚本的所有标准输出信息重定向到out.txt文件 exec 1>out.txt e.g.关闭文件描述符3 exec 3>&-

环境变量

bash shell使用环境变量(environment variable)来存储有关shell的会话和工作环境,并将这些信息存储在内存中,以便程序或shell脚本能够轻松访问到这些数据,一般情况下,环境变量均使用大写的变量名

全局环境变量

全局环境变量该shell和其创建的子shell中均有效,以下为查询全局环境变量的命令

  • printenv查看所有全局环境变量
    • -0 每行输出末尾输出空字符(null),而不是换行符,以便其他程序解析输出
  • env查看所有全局环境变量
  • printenv 变量名查看指定环境变量
  • echo $变量名查看指定环境变量

定义全局环境变量需要使用export关键字

  • export 变量名=值 定义全局环境变量
  • unset 变量名 删除变量

父shell中创建的全局环境变量在子shell中也有效,且如果此时在子shell中修改该变量的值,不会影响其在父shell中的值,修改后的值只在子shell中有效

e.g.定义一个普通全局变量,在子shll中修改值,然后返回父shell读取值 $ my_variable="I am Global now" $ export my_variable #声明为全局变量 $ bash #切换到子shell $ echo $my_variable #子shell可以访问该变量 I am Global now $ my_variable="Null" #在子shell中修改变量值 $ export my_variable #即便用export命令也无法修改其在父shell中的值 $ echo $my_variable Null #该值在子shell中有效 $ exit exit $ $ echo $my_variable #父shell中值不变 I am Global now
e.g.环境变量支持数组 myArray=(one two three four five) echo ${myArray[2]} echo ${myArray[*]} unset myArray #删除数组

局部环境变量

局部环境变量只能在定义它们的shell中有效,父shell中的局部环境变量无法在子shell中访问到。Linux中没有只显示局部环境变量的命令,但可以通过set命令查看所有环境变量,包括局部变量、全局变量以及用户定义变量

定义局部环境变量的方法和定义局部自定义变量的方法一样,不需要加export关键字,只需要将变量名大写用于识别该变量是一个环境变量即可

MY_PORT=2021 echo $MY_PORT bash #切换到子shell echo $MY_PORT #无法访问该变量

Bash中的环境变量

以下为bash shell种已经定义好的环境变量,可以直接调用,有些环境变量的值为空,因此执行set命令时不一定会列出所有变量

变量名 说明
HOME 当前用户的主目录
UID 当前用户的真实用户ID(数字形式)
EUID 当前用户的有效用户ID(数字形式)
IFS shell用来将文本字符串分割成字段的一系列字符
MAIL 当前用户收件箱的文件名
MAILPATH 冒号分隔的当前用户收件箱的文件名列表
OPTARG getopts命令处理的最后一个选项参数值
OPTIND getopts命令处理的最后一个选项参数的索引号
PATH shell查找命令的目录列表,由冒号分隔
PS1 shell命令行界面的主提示符
PS2 shell命令行界面的次提示符
CDPATH cd命令的搜索路径,切换路径时,cd命令会先尝试在当前目录下查找指定目录并切换过去,如果当前目录下并没有指定的目录,则cd命令会前往该变量设置的路径下查找并切换,可以指定多个路径用冒号分隔
BASH 当前shell实例的全路径名
BASH_ALIASES 含有当前已设置别名的关联数组
BASH_ARGC 含有传入子函数或shell脚本的参数总数的数组变量
BASH_ARCV 含有传入子函数或shell脚本的参数的数组变量
BASH_CMDS 关联数组,包含shell执行过的命令的所在位置
BASH_COMMAND shell正在执行的命令或马上就执行的命令
BASH_ENV 设置了的话,bash脚本会在执行前读取变量中的文件,读取文件中设置的变量,并执行其中的命令
BASH_EXECUTION_STRING 使用bash -c选项传递过来的命令
BASH_LINENO 含有当前执行的shell函数的源代码行号的数组变量
BASH_REMATCH 只读数组,在使用正则表达式的比较运算符=~进行肯定匹配(positive match)时,包含了匹配到的模式和子模式
BASH_SOURCE 含有当前正在执行的shell函数所在源文件名的数组变量
BASH_SUBSHELL 当前子shell环境的嵌套级别(初始值是0)
BASH_VERSINFO 含有当前运行的bash shell的主版本号和次版本号的数组变量
BASH_VERSION 当前运行的bash shell的版本号
BASH_XTRACEFD 若设置成了有效的文件描述符(0、1、2),则’set -x’调试选项生成的跟踪输出,可被重定向。通常用来将跟踪输出到一个文件中
BASHOPTS 当前启用的bash shell选项的列表
BASHPID 当前bash进程的PID
COLUMNS 当前bash shell实例所用终端的宽度
COMP_CWORD COMP_WORDS变量的索引值,后者含有当前光标的位置
COMP_LINE 当前命令行
COMP_POINT 当前光标位置相对于当前命令起始的索引
COMP_KEY 用来调用shell函数补全功能的最后一个键
COMP_TYPE 一个整数值,表示所尝试的补全类型,用以完成shell函数补全
COMP_WORDBREAKS Readline库中用于单词补全的词分隔字符
COMP_WORDS 含有当前命令行所有单词的数组变量
COMPREPLY 含有由shell函数生成的可能填充代码的数组变量
COPROC 占用未命名的协进程的I/O文件描述符的数组变量
DIRSTACK 含有目录栈当前内容的数组变量
EMACS 设置为’t’时,表明emacs shell缓冲区正在工作,而行编辑功能被禁止
ENV 如果设置了该环境变量,在bash shell运行之前会读取该变量指定路径中的文件,如果未设置,Bash 会默认读取用户的”HOME/.bashrc”文件以设置环境变量,该变量方便用户自定义bash启动时的环境变量(仅用于当bash shell以POSIX模式被调用时)
FCEDIT 供fc命令使用的默认编辑器
FIGNORE 在进行文件名补全时可以忽略后缀名列表,由冒号分隔
FUNCNAME 当前执行的shell函数的名称
FUNCNEST 当设置成非零值时,表示所允许的最大函数嵌套级数(一旦超出,当前命令即被终止)
GLOBIGNORE 冒号分隔的模式列表,定义了在进行文件名扩展时可以忽略的一组文件名
GROUPS 含有当前用户属组列表的数组变量
histchars 控制历史记录扩展,最多可有3个字符
HISTCMD 当前命令在历史记录中的编号
HISTCONTROL 控制哪些命令留在历史记录列表中
HISTFILE 保存shell历史记录列表的文件名(默认是.bash_history)
HISTFILESIZE 最多在历史文件中存多少行
HISTTIMEFORMAT 如果设置了且非空,就用作格式化字符串,以显示bash历史中每条命令的时间戳
HISTIGNORE 由冒号分隔的模式列表,用来决定历史文件中哪些命令会被忽略
HISTSIZE 最多在历史文件中存多少条命令
HOSTFILE shell在补全主机名时读取的文件名称
HOSTNAME 当前主机的名称
HOSTTYPE 当前运行bash shell的机器
IGNOREEOF shell在退出前必须收到连续的EOF字符的数量(如果这个值不存在,默认是1)
INPUTRC Readline初始化文件名(默认是.inputrc)
LANG shell的语言环境类别
LC_ALL 定义了一个语言环境类别,能够覆盖LANG变量
LC_COLLATE 设置对字符串排序时用的排序规则
LC_CTYPE 决定如何解释出现在文件名扩展和模式匹配中的字符
LC_MESSAGES 在解释前面带有$的双引号字符串时,该环境变量决定了所采用的语言环境设置
LC_NUMERIC 决定着格式化数字时采用的语言环境设置
LINENO 当前执行的脚本的行号
LINES 定义了终端上可见的行数
MACHTYPE 按“CPU-公司-系统”(CPU-company-system)格式定义的系统类型
MAPFILE 一个数组变量,当mapfile命令未指定数组变量作为参数时,它存储了mapfile所读入的文本
MAILCHECK shell查看新邮件的频率(以秒为单位,默认值是60)
OLDPWD shell之前的工作目录
OPTERR 设置为1时,bash shell会显示getopts命令产生的错误
OSTYPE 定义了shell所在的操作系统
PIPESTATUS 含有前台进程的退出状态列表的数组变量
POSIXLY_CORRECT 设置了的话,bash会以POSIX模式启动
PPID bash shell父进程的PID
PROMPT_COMMAND 设置了的话,在命令行主提示符显示之前会执行这条命令
PROMPT_DIRTRIM 用来定义当启用了\w或\W提示符字符串转义时显示的尾部目录名的数量。被删除的目录名会用一组英文句点替换
PS3 select命令的提示符
PS4 如果使用了bash的-x选项,在命令行之前显示的提示信息
PWD 当前工作目录
RANDOM 返回一个0~32767的随机数(对其的赋值可作为随机数生成器的种子)
READLINE_LINE 当使用bind –x命令时,存储Readline缓冲区的内容
READLINE_POINT 当使用bind –x命令时,表示Readline缓冲区内容插入点的当前位置
REPLY read命令的默认变量
SECONDS 自从shell启动到现在的秒数(对其赋值将会重置计数器)
SHELL bash shell的全路径名
SHELLOPTS 已启用bash shell选项列表,列表项之间以冒号分隔
SHLVL shell的层级;每次启动一个新bash shell,该值增加1
TIMEFORMAT 指定了shell的时间显示格式
TMOUT select和read命令在没输入的情况下等待多久(以秒为单位)。默认值为0,表示无限长
TMPDIR 目录名,保存bash shell创建的临时文件

环境变量配置文件

用户登录shell时,shell程序会读取环境变量配置文件作为初始化环境,用户可以修改这些文件来指定shell启动时自动执行的任务,以及指定一些环境变量值

全局配置文件

对所有用户生效

  • /etc/profile 主要的配置文件,也是用户登录时最先读取的文件,用于保存全局环境变量和shell参数,并调用其他配置文件
  • /etc/profile.d/.sh后缀文件 由profile调用
  • /etc/bashrc 用于保存bash相关的全局环境变量、函数、命令别名,如PS1等bash终端的环境

/etc/profile配置文件是最主要的配置文件,其中几个常见变量的作用:

变量 作用
USER 当前用户
LOGNAME 当前用户名,配置文件中LOGNAME=$USER,因此同USER
MAIL 用户邮箱
HOSTNAME 主机名
HISTSIZE 历史命令保存条数
umask 设置文件默认权限
用户配置文件

只对单一用户生效,每个用户都有自己的配置文件,root用户位于/root下,普通用户位于/home/用户名下,默认隐藏,需要使用ls -a命令才能看见文件

  • $HOME/.bash_profile   用户个人的/etc/profile文件,保存用户自定义的环境变量,该文件会在用户登录时读取,且一般会在文件中调用其他配置文件(如下面的.bashrc)
  • $HOME/.bashrc  写有用户自定义的命令别名、函数等,该文件会在打开一个新的交互式bash时都读取一次
  • $HOME/.bash_logout  用户注销时执行的环境变量配置文件,可以写一些我们希望系统关机时执行的操作,如备份日志等
  • $HOME/.bash_history  记录用户执行过的命令

部分Linux发行版还可能会提供如:$HOME/.bash_login、$HOME/.profile等文件,其作用一般和$HOME/.bash_profile一样,shell一般会优先执行$HOME/.bash_profile

不重启就使配置文件生效

直接执行任一命令

  • source 文件名
  • .配置文件名

不同启动方式读取的配置文件

日常使用时,启动bash一般分为3种方式,每种方式都会读取不同的配置文件

  1. 登录用户账户时作为默认启动的交互式shell
  2. 通过bash等命令切换创建的非登录交互式子shell
  3. 执行脚本或任务启动的非交互式shell,如:执行脚本、使用at、cron等命令后台执行定时任务、通过脚本或()等方式启动的子shell

作为默认登录shell启动时,bash会依次读取全局/etc/profile文件,以及用户的$HOME/.bash_profile文件,而绝大多数Linux发行版用户$HOME/.bash_profile文件会调用$HOME/.bashrc文件,而$HOME/.bashrc文件又会调用/etc/bashrc文件,因此一般情况下,登录shell会依次加载/etc/profile、$HOME/.bash_profile、$HOME/.bashrc、/etc/bashrc文件

而作为非登录shell启动时,它不会访问/etc/profile、$HOME/.bash_profile等文件,而只会依次读取/etc/bashrc、$HOME/.bashrc文件,$HOME/.bashrc文件虽然包含了对/etc/bashrc的调用,但依旧会首先加载/etc/bashrc文件,$HOME/.bashrc文件对/etc/bashrc的再次调用并不会引起异常。由于非登录shell是从父shell启动的,因此/etc/profile、$HOME/.bash_profile文件中的全局环境变量也会从父shell中继承下来。

作为非交互式shell启动时,bash shell会检查BASH_ENV 环境变量,如果该变量指定了文件,shell会执行该文件里的命令,并读取里面设置的变量。但在绝大多数Linux发行版中,这个变量一般为空,因此非交互式shell一般不会读取配置文件。但该方式启动的shell,若脚本直接将当前shell作为脚本的执行shell,则所有当前shell中已加载的局部变量和全局变量,都可以在脚本中使用。如果脚本中通过诸如进程列表等方式创建了子shell,而其父shell在其加载的/etc/profile、$HOME/.bashrc等(具体加载哪些文件取决于该父shell的启动方式)文件中使用export声明了全局变量,用于执行脚本的子shell会继承这些变量。

环境变量的持久化

通过赋值方式创建的变量保持于内存中,如果退出shell这些变量会失效,可以将这些环境变量写于配置文件中实现变量的持久化。

对于所有用户都可能用到的环境变量,尽量避免将全局的环境变量放在/etc/profile文件中,该文件会在系统升级时被覆盖,可以在/etc/profile.d目录中创建一个.sh结尾的文件,将全局环境变量放在该文件中,然后再在profile文件中加载该目录下的内容。

对于个人用户,可以将环境变量存放于$HOME/.bashrc文件中,但注意,如果需要在脚本中调用某些自定义环境变量,由于脚本启动的一般是非交互式shell,所以需要先检查是否设置了BASH_ENV 环境变量,如果有则需要将环境变量存放在对应的文件中;如果没有,则考虑在其父shell会读取的文件中将环境变量声明为export或在脚本中设置一个新的。

终端控制

ANSI转义字符序列

为了控制终端的显示和行为,ANSI标准定义了一系列用于控制终端输出选项的字符序列,称为ANSI转义字符序列(Escape Sequence)。这些转义字符序列中ASCII码表0到31(0x00–0x1F)之间的字符称为C0控制字符,C0字符通常用于基本的文本控制和终端操作,如:回车(CR)、换行(LF)、制表符(TAB)等。由于原有的控制字符功能不够全面,为了实现更多的终端控制功能,ASCII码表扩展出了值在128-159(0x80–0x9F)之间的C1控制字符,C1控制字符提供了更多扩展的高级控制功能,包括光标控制、颜色设置、字符集切换、设备控制等

CSI序列

C1控制字符中用于控制光标、设置文本样式和颜色以及实现屏幕的清除、局部刷新等操作的控制字符称为CSI(Control Sequence Introducer)字符,CSI字符序列以ESC(\x1b)开头,并紧接一个[(代表CSI),将ESC转换为ASCII码后,控制字符在代码中常写为\e[\x1b[\033[,完整的CSI控制语句为:
\e[参数1;参数2;参数3…函数名

  • \e[ 为转义字符引导头
  • 中间参数部分可以由0个或多个数字组成,多个数字间用分号分隔
  • 函数名为需要调用的CSI函数功能名称
如:\e[0;4;34m # \e[ 为转义字符引导头 # m为函数名 # 0;4;34为函数参数,相当于 m(0,4,34)

函数

CSI提供了很多用于光标控制、屏幕控制、字符渲染的函数

光标控制
函数 举例 功能
A \e[nA 光标上移n行(默认1)
B \e[nB 光标下移n行(默认1)
C \e[nC 光标左移n行(默认1)
D \e[nD 光标右移n行(默认1)
E \e[nE 光标下移n行(默认1),非标准
F \e[nF 光标上移n行(默认1),非标准
G \e[nG 光标移动至当前行的n列,非标准
H \e[x;yH 光标移动到x行y列
s \e[s 保存光标位置
u \e[u 取出光标位置
l \e[?25l 隐藏光标
h \e[?25h 显示光标
屏幕控制
函数 举例 功能
J \e[nJ 清除指定范围内的屏幕,0为光标位置到屏幕末尾,1为光标位置到屏幕开头,2为全屏幕
K \e[nK 清除该行的指定范围,0为光标到行尾,1为光标至行头,2为整行
S \e[nS 整页向上滚动n行,非标准
T \e[nT 整页向下滚动n行,非标准

修改字体样式

终端中最为常用的转义序列还是用于修改终端字体颜色、背景颜色和显示效果的函数和参数,即SGR(Set graphics mode)函数m,常用于配合echo语句输出不同样式的提示信息,修改终端样式的转义字符序列一般为:
\e[参数m

在指定多个参数时,转义字符序列通常为:
\e[字体样式;字体颜色;背景色m

参数不分先后顺序,指定多个同类样式时,后面的样式将覆盖前面的样式(如:指定多个背景色,只有最后一个背景色会生效)

1.输出红色字符串 "RedColor" echo -e "\e[31mRedColor \e[0m" 2.输出高亮、红色字体、背景为淡黄色的字符串"aaa" echo -e "\e[1;31;103maaaa\e[0m" # 末尾的\e[0m用于删除所有设置的属性(包括颜色、加粗、闪烁等效果)使后续输出内容恢复默认属性 # \e可替换为\033或\x1B
恢复默认

\e[0m表示取消所有设置的属性(包括颜色、加粗、闪烁等效果),使后续输出内容恢复默认属性

文字效果
设置效果代码 效果 取消效果代码
1 加粗高亮 21
2 变暗(未被广泛支持)
3 斜体(未被广泛支持)
4 下划线 24
5 缓慢闪烁,低于每分钟150次 25
6 快速闪烁,每分钟150次以上(未被广泛支持)
7 反转背景色和前景色 27
8 隐藏,常用于密码(未被广泛支持) 28
9 划掉文字(未被广泛支持)
10 默认字体
11-19 替代字体
20 尖角体
字体颜色(前景色)

以下颜色在不同终端中被定义了不同的RGB值,且没有一个通用标准,因此不同终端中的样式可能有所不同,详情可参考:ANSI escape code

代码 颜色 代码 颜色
39 默认颜色
30 黑色 90 灰色
31 红色 91 亮红色
32 绿色 92 亮绿色
33 黄色 93 亮黄色
34 蓝色 94 亮蓝色
35 品红 95 亮品红色
36 青色 96 亮青色
37 白色 97 亮白色
字体背景色

每一个颜色都和上面相同,只是他们将作为背景色使用

代码 颜色 代码 颜色
49 默认颜色
40 黑色 100 灰色
41 红色 101 亮红色
42 绿色 102 亮绿色
43 黄色 103 亮黄色
44 蓝色 104 亮蓝色
45 品红 105 亮品红色
46 青色 106 亮青色
47 白色 107 亮白色
修改文件目录颜色

想在使用ls命令后使各种不同类型文件显示不同颜色,参考dircolors命令

OSC序列

OSC(Operating System Command)是一类用于执行操作系统级别命令的控制序列,通常用来设置终端标题,或与系统剪贴板进行交互等。OSC和CSI都是ANSI转义序列的一部分,其中CSI是C1控制字符集的一部分,而OSC是独立的多字符集合,不属于C1控制字符集

与CSI字符序列语法类似,OSC序列也是以ESC开头,随后,OSC序列后紧接一个],后接一个数字标识符,然后是一个命令或参数,最后以\a(BEL)或\e\(ESC+\)结尾,完整的OSC语法为:
\e]函数标识值;参数1;参数2;参数3…\a

  • \e]是OSC控制序列的引导头
  • 函数值功能参考下表
  • \a 标识控制序列结束

OSC函数值

OSC字符序列也可以修改终端的文本颜色,背景色等样式,与CSI不同的是,OSC字符序列控制的是全局的终端样式,而CSI序列只修改跟随在CSI后的文本样式,而不会修改已经输出完毕的文本样式。但OSC在一些终端中(尤其远程终端中)只支持修改终端窗口的标题等,而不支持修改光标、文本颜色等样式

说明
0 更改窗口标题和标签栏标题
1 设置终端窗口的图标名
2 仅更改窗口标题
10 更改字体颜色(前景色)
11 更改背景色
12 更改光标颜色
52 将文本写入剪贴板
104 重置所有颜色
以下OSC序列只会临时修改终端设置,在命令执行完毕后可能会立即恢复终端默认样式,以至于无法查看到样式变化,如果需要通过OSC长久修改终端标题等设置,请修改PS1变量的值 1.更改窗口标题和标签栏标题 echo -e "\e]0;新标题\a" 2. 仅更改窗口标题 echo -e "\e]2;新的窗口标题\a" 3.更改字体颜色为#00ff00 echo -e "\e]10;#00ff00\a" 4.更改光标颜色为#ff00ff echo -e "\e]12;#ff00ff\a"

修改命令提示符

默认情况下的命令提示符通常为[用户名@主机名 当前目录]主提示符(不同Linux发行版可能有所不同),如:[root@localhost etc]#,该默认值由系统定义的变量PS1决定,可通过echo $PS1 查看当前配置,也可以通过修改PS1变量自定义命令提示符,如:

PS1='[\u@\t \w]#' 命令提示符将修改为: [root@15:21:13 /home/user1]#

通过修改PS1变量自定义的命令提示符,只在本次登录有效,系统重启后将失效,需要永久修改命令提示符,可以将上述语句写入配置文件/etc/bashrc中,若只想修改某个用户的终端,可以修改该用户家目录下的.bashrc配置文件中的PS1变量,修改完后可以使用source 文件名命令重新加载配置文件,使配置立即生效

修改命令提示符文本颜色

修改命令提示符文本颜色时,PS1变量值中的CSI语法需要使用[ ]进行包裹,并且”[ ]”需要使用转义字符进行转义,避免bash解析其中的语法,即语法为:

\[\e[颜色码m\]

如在/etc/bashrc文件最后一行添加语句: PS1='\[\e[0m\][\[\e[36m\]\u\[\e[34m\]@\h \[\e[36m\]\W\[\e[0m\]]\[\e[95m\]#\[\e[0m\]' 终端提示符将变为: [root@myLinux etc]#

以下是自定义命令提示符时常用的转义字符:

转义符 作用
\u 显示当前用户名
\h 显示简写主机名(不包含域名,该值来自/etc/hostname文件)
\H 显示完整的主机名(通常包括域名)
\w 显示当前所在路径的完整名称
\W 显示当前所在目录(路径的最后一个路径段)
\# 显示本次会话已经执行的命令个数
\$ 显示命令主提示符,root用户提示符为”#”,普通用户为”$”
\d 显示当前系统日期,格式为”星期 月 日”
\t 显示24小时制时间,格式为”HH:MM:SS”
\T 显示12小时制时间,格式为”HH:MM:SS”
\A 显示24小时制时间,格式为”HH:MM”
\n 换行符
\[ 和 \] 用于定义非打印字符的开始和结束(主要用于设置颜色,避免光标定位错误)

终端标题

虽然PS1主要用于控制终端的命令提示符,但我们可以在PS1中嵌入OSC转义序列来控制终端的标题。与CSI类似,PS1变量值中的OSC序列也需要使用[ ]进行包裹,并且”[ ]”需要使用转义字符进行转义,避免bash解析其中的语法

1.某个debian发行版的默认PS1值 PS1="\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w\$" 其中: \[\e]0;\u@\h: \w\a\]为OSC序列,定义终端标题 ${debian_chroot:+($debian_chroot)}用于在命令提示符中提示用户当前是否处于chroot环境 2.修改终端标题 PS1='\[\e]0;终端标题\a\]\t@debian\$'
PS1的字符串值请用单引号或双引号包裹,否则可能出现Bash解析错误的情况

本地终端登录前的欢迎信息

需要修改本地终端登录时显示的信息,可修改配置文件/etc/issue,配置文件中可使用的转义字符:

转义符 作用
\d 显示当前系统日期
\t 显示当前系统时间
\s 显示操作系统名称
\l 显示登录的终端号
\m 显示硬件架构信息,如i386等
\n 显示主机名
\o 显示域名
\r 显示内核版本
\u 显示当前用户登录的序列号
最小化安装的Centos7默认配置通常为: \S Kernel \r on an \m

远程终端登录前的欢迎信息

修改远程终端登录时显示的信息,可修改配置文件/etc/issue.net,该文件不支持上述本地终端登录可用的转义字符,需要显示此欢迎信息,需要在配置文件/etc/ssh/sshd_config中加入Banner /etc/issue.net配置内容(需要重启生效)

/etc/ssh/sshd_config文件添加配置位置: #UseLogin no #UsePrivilegeSeparation sandbox #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 #ShowPatchLevel no #UseDNS yes #PidFile /var/run/sshd.pid #MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none #VersionAddendum none

# no default banner path
#Banner none
Banner /etc/issue.net

# Accept locale-related environment variables

终端登录成功后的提示信息

配置文件位于/etc/motd(Message of the Day文件),用于在远程和本地终端登录成功后显示提示信息,文件通常默认为空

在ubuntu中,登录提示文本MOTD通常是动态生成的,配置文件位于/etc/update-motd.d/中,包含一系列脚本,这些脚本会生成系统信息(如负载、内存使用等)并在登录时显示,脚本名前的数字表示脚本执行顺序,相同数字的脚本会按照脚本名字母顺序依次执行,执行顺序也决定了这些文本的显示顺序

update-motd.d目录下可能会有以下脚本文件: 00-header 10-help-text .... 91-release-upgrade 98-fsck-at-reboot 如果需要自定义提示信息,可以在该目录下新建一个脚本,如: 1. 创建一个脚本 99-custom 2. 添加需要打印的信息 #!/bin/sh echo "欢迎使用shiwivi的 Ubuntu 服务器!" 3. 给脚本添加可执行权限 sudo chmod +x /etc/update-motd.d/99-custom 如果不想显示某个脚本的内容,可以通过修改文件名(例如在文件名中添加 .disabled)来禁用特定脚本,或者修改脚本中的输出语句
上一篇:Shell脚本语言
下一篇:Linux常用工具命令
z z z z z