<
  • 主题:
  • + -
  • 清除背景
  • 禁用背景
目 录
  1. 1. Bash常用快捷键
  2. 2. shell介绍
    1. 2.1. shell简介
    2. 2.2. 切换shell
    3. 2.3. 父子shell
      1. 2.3.1. 查看父子shell的创建关系
      2. 2.3.2. 父子shell的使用场景
  3. 3. 内建命令与外部命令
    1. 3.1. 外部命令
    2. 3.2. 内建命令
    3. 3.3. 命令的执行优先级
    4. 3.4. 命令分组执行
    5. 3.5. 创建协程
  4. 4. Bash的内建命令
    1. 4.1. Bash的启动方式
    2. 4.2. history历史命令
      1. 4.2.1. 调用历史命令
    3. 4.3. alias自定义命令名
    4. 4.4. type查询命令类型
    5. 4.5. command执行原始命令
    6. 4.6. set管理shell功能
    7. 4.7. :空操作
    8. 4.8. mapfile将标准输入存储到数组
    9. 4.9. exec命令
      1. 4.9.1. 替换当前shell进程
      2. 4.9.2. 修改文件描述符
  5. 5. <()与>()重定向
  6. 6. 通配符
    1. 6.1. 常用通配符
    2. 6.2. {..}花括号扩展
    3. 6.3. 扩展通配符
  7. 7. POSIX字符类
  8. 8. 环境变量
    1. 8.1. 全局环境变量
    2. 8.2. 局部环境变量
    3. 8.3. Bash中的环境变量
    4. 8.4. 环境变量配置文件
      1. 8.4.1. 全局配置文件
      2. 8.4.2. 用户配置文件
      3. 8.4.3. 不重启就使配置文件生效
    5. 8.5. 不同启动方式读取的配置文件
    6. 8.6. 环境变量的持久化
  9. 9. 终端控制
    1. 9.1. ANSI转义字符序列
    2. 9.2. CSI序列
    3. 9.3. 函数
      1. 9.3.1. 光标控制
      2. 9.3.2. 屏幕控制
    4. 9.4. 修改字体样式
      1. 9.4.1. 恢复默认
      2. 9.4.2. 文字效果
      3. 9.4.3. 字体颜色(前景色)
      4. 9.4.4. 字体背景色
      5. 9.4.5. 修改文件目录颜色
    5. 9.5. OSC序列
    6. 9.6. OSC函数值
    7. 9.7. 修改命令提示符
    8. 9.8. 终端标题
    9. 9.9. 本地终端登录前的欢迎信息
    10. 9.10. 远程终端登录前的欢迎信息
    11. 9.11. 终端登录成功后的提示信息
  10. 10. tput 终端控制标准化接口
    1. 10.1. 介绍
      1. 10.1.1. Terminfo数据库
      2. 10.1.2. tput命令工具
      3. 10.1.3. 与CSI控制序列的区别
    2. 10.2. tput命令
    3. 10.3. 基本terminfo功能
    4. 10.4. 颜色控制
    5. 10.5. 文字样式控制
    6. 10.6. 光标控制
    7. 10.7. 擦除行
    8. 10.8. 插入/删除行与列
    9. 10.9. 切换屏幕缓冲区

Shell与Bash

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

Bash常用快捷键

修改快捷键参考stty命令

快捷键 说明
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用户的命令提示符
> 次提示符
\ 延续字符
GUN官方的Bash文档:Bash Reference Manual

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的内建命令

Bash的启动方式

bash [选项] [脚本文件] [参数]

  • -c “字符串”:执行字符串中的命令
  • -i 启动交互式bash
  • -l 或–login:模拟登录Shell,加载用户环境配置(如/etc/profile、~/.bash_profile等文件)
  • -s 从标准输入读取命令,通常结合管道和重定向符传递脚本内容
  • -r 以受限模式(Restricted Shell)启动bash,限制用户操作,用户将无法执行以下操作:
    • 使用cd命令改变当前目录
    • 设置或更改 $PATH, $SHELL, $ENV, $BASH_ENV 等变量
    • 使用 exec 执行其他程序
    • 使用重定向 >、>> 等创建/改写文件
    • 调用包含 / 的命令(如 /bin/ls)
  • -v 逐行显示每一行命令(不将变量替换为值),然后执行,通常用来观察脚本的执行流程
  • -x 逐行显示每一行命令,并将命令中的变量替换为实际值,输出的命令行首会添加+前缀,通常用来调试变量、参数值是否正确
  • -n 检查语法错误但不执行,仅验证脚本语法正确性
  • -e 严格模式,遇到错误立即退出,防止错误蔓延
  • -u 遇到未定义变量时报错,检查是否有未定义变量

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 只显示命令类型,而不显示命令路径,返回类型可能有:alias(别名)、builtin(内建命令)、file(外部命令)、function(定义的函数)、keyword(shell保留的关键字)
  • -p (小写)只显示命令路径,如果命令是shell内建命令或别名,使用此选项不会有输出
  • -P (大写)在PATH中查找该命令,无论该命令是何种类型,都返回其路径,该选项常被用来在shell脚本中查询并精确执行某命令,如$(type -P curl)
  • -f 不在shell函数中查找,即不将命令视为函数来查找

command执行原始命令

当系统中创建了alias别名和shell函数时,如果用户不想执行这些别名或shell函数,可以使用command来执行原始的Linux命令,常用来在脚本中安全地执行某个命令,而不受用户环境影响

command [选项] 命令 [参数]

  • 默认情况下,通过command执行的命令会无视alias或shell函数,执行系统原装的命令,默认前往PATH路径下查找该命令,包括用户自行在PATH中添加的路径
  • -p 在PATH中查找命令时,无视用户添加的PATH路径,而只在系统默认的PATH路径下查找(通常是/bin和/usr/bin等系统默认命令路径),以免用户在PATH中添加了非系统原生的命令路径
  • -v 显示关于命令的信息(是别名、函数、内置命令还是磁盘上的可执行文件),常用来在脚本中测试是否安装了某个命令,相较于使用type -P检测,command -v是POSIX标准,更推荐在可移植脚本中使用
  • -V 显示更详细的命令描述信息
1.command可以绕开alias和shell中定义的函数, 如:ls命令通常是以下命令别名 alias ls='ls --color=auto' 为了避免不同用户定义了不同ls命令别名,可以选择用command来规避这些影响 command ls # 这将执行原始的ls命令,而不带--color=auto选项 2.通过command -v检测某个命令是否存在 if command -v apt &>/dev/null; then sudo apt install -y ....... elif command -v yum &>/dev/null; then sudo yum install -y ....... elif command -v dnf &>/dev/null; then sudo dnf install -y ....... else echo "不支持当前包管理器安装" fi

set管理shell功能

set命令用来查看或修改当前shell的设置,如打开或关闭shell的调试、安全、错误处理等功能,修改shell的位置变量等,它的功能十分强大,这里只整理部分可能用到的常用功能

set [-或+] [选项] [-‌-] [参数]

  • 不添加任何选项和参数时,set会打印所有shell变量和函数,包括环境变量、用户定义变量、位置参数、全局函数等
  • 当为set不提供任何选项,只提供参数时,set会将位置参数$1,$2..设置为对应参数
  • -会开启一个功能选项,+会关闭一个选项
    • e 一旦任何命令返回非0状态,立刻退出脚本
    • v 逐行显示每一行命令(不将变量替换为值),然后执行
    • x 逐行显示每一行命令,并将命令中的变量替换为实际值,输出的命令行首会添加+前缀
    • u 使用未定义变量时报错
    • n 只做语法检查不执行脚本
  • [-/+o 特殊控制名] 开启或关闭特殊设置
    • history:启用命令历史记录,对于交互式shell,该配置默认开启
    • ignoreeof:交互式shell在读取到EOF时不会退出
    • pipefail:管道中任意命令失败时,整个管道视为失败
    • vi:使用vi风格的编辑界面,这会影响用于read -e 的编辑界面
    • emacs:使用emacs风格的编辑界面,这会影响用于read -e 的编辑界面
1.set可以用来修改位置参数 set -‌- 10 20 30 set会将$1、$2、$3设置为10,20,30 set -‌- echo "参数个数:$#" set会将所有位置参数设置为unset,即清空它们 $1,$2..会被清空,$#参数个数为0 2.set命令可以在脚本中临时开启某些调试功能,方便调试某一段代码 这可以视为bash -vxu命令的局部版本,只在某一段代码中开启这些功能 #!/bin/bash echo "开始" set -x #开启x选项的功能,逐行显示命令和变量值 echo "调试这句" name="Alice" echo "你好,$name" set +x #关闭x选项的功能 echo "结束" 3.在脚本开头添加set -euo pipefail,用来开启严格模式 之后,脚本中任意命令失败都会退出脚本 使用未定义的变量时报错 管道中任意命令失败时,整个管道视为失败

:空操作

不执行任何操作,总是返回成功(退出状态码为0),一般可用作占位符

1.可用作条件语句中的占位符 if 条件; then : #占位符,不执行任何操作 else echo "Not true" fi 2.在某些旧脚本中可用来隐藏输出 : $(command) 将command命令输出结果传参给: 而:命令本身无输出,因此上述语句等价于command > /dev/null

mapfile将标准输入存储到数组

该用于将输入(如文件或管道数据)逐行读取到数组中,该命令的效率高于while read循环

mapfile [选项] [数组名]

  • 从标准输入或文件中读取行数组中,如果未指定数组,则使用默认的数组名MAPFILE
  • -d “行终止符”:指定每个行的终止符,而不是默认的换行符,-d “”指定空字符串,将使用NUL作为行终止符
  • -n 值N:只输入N行,如果值为0将输入所有行到数组
  • -u 文件描述符:从该文件描述符指向的文件读取数据
  • -O 索引值:指定索引值,从该值开始存储,默认从0存储
  • -s 值N:跳过前N行,不存储到数组
  • -t 输入时删除行尾的行终止符
  • -C 回调函数名:在每次读入行时执行回调函数,回调函数中可以使用$1(下一个存入的数组索引),$2(要存入的行内容)引用对应的数据,mapfile命令会自动传递这2个参数,如果不指定-c,默认每5000行执行一次回调,以避免性能问题
  • -c 值N:和-C选项搭配使用,每读取N行执行一次回调
1.默认情况,mapfile会将每行的换行符也作为数组元素,因此往往需要-t删除换行符 mapfile -t arr < file.txt 2.指定回调函数时,可以用$1和$2引用数组索引和值,索引会随着-O选项的值产生偏移 arr=(aaaa bbbbb) process_line() { echo "下一个索引: $1" #依次输出3,4 } mapfile -t -O 3 -c 1 -C process_line arr <<EOF hello shell EOF

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特有的进程替换(Process Substitution)语法,用于将命令的输入或输出“伪装”为一个文件路径,以供其他命令使用。

使用时,Bash会创建一个临时文件描述符,然后将该文件名作为参数传入命令,该语法适合一些需要文件名或路径作为参数的命令中,如:cat、diff、read、tar等

1.diff需要文件路径作为输入 diff <(sort file1.txt) <(sort file2.txt) 2.结合mapfile将命令结果存入数组 mapfile -t arr < <(find /dst -mindepth 1 -maxdepth 1 -type d) 3.处理输出 tail -f server.log > >(grep "ERROR" > errors.txt)

通配符

通配符(Wildcard)是shell中用来进行文件或路径匹配的操作符,常用ls、cp、rm等命令,它的功能和正则表达式(Regular Expression)很像,但本质上是不同的工具

常用通配符

符号 作用
* 匹配任意长度的任意字符,可以为空
? 匹配单个任意字符
[abc] 匹配a或b或c中的任意一个字符,相当于”或”
[!abc]或[^abc] 匹配不包括a、b、c在内的任意一个字符,相当于”非”
1.*可以代表任意数量的字符,包括空 ls file*.txt 会列出file.txt以及file后还有其他字符的文件 fileA.txt file20201205.txt 2.?代表单个任意字符,?可多写代表多个任意字符 file?.txt匹配如:fileA.txt file????.txt匹配file2021.txt 3.[xxx]代表关系或 file[ABC].txt 匹配 fileA.txt fileB.txt fileC.txt 4.[!xxx]代表关系非 file[!BC].txt 匹配 fileA.txt,但不包括 fileB.txt fileC.txt

{..}花括号扩展

花括号符{..}可以扩展花括号中的值,通常需要和其他字符串搭配,用来快速和其他字符串生成组合值,该语法为Bash独有,大多数shell不支持

语法 作用
{a,b,c} 将a,b,c扩展到其他字符串中
{1..4}或{a..z} 将该区间的所有值或字母拓展到其他字符串中
1.创建3个文件dir_A,dir_B,dir_C mkdir dir_{A,B,C} 2.移除file.txt和file.log rm -f file.{txt,log} 3.创建5个文件file_1,file_2,file_3,file_4,file_5 touch file_{1..5} 4.执行一个循环 for i in {1..6};do echo $i done 5.file[abc]和file{a,b,c}的行为不同 file[abc]代表filea或fileb或filec file{a,b,c}代表filea、fileb、filec,是"与"的关系 []通常用来校验或查询是否有该文件 {}通常用来创建或移除已有文件

扩展通配符

扩展通配符需要先执行shopt -s extglob来启用extglob扩展模式

通配符 作用 示例
?(pattern) 匹配0次或1次 pattern file?(.bak) 匹配 file 或 file.bak
*(pattern) 匹配0次或多次 pattern dir*(/tmp) 匹配 dir、dir/tmp、dir/tmp/tmp…
+(pattern) 匹配1次或多次 pattern log+(.old) 匹配 log.old、log.old.old…
@(pattern1|pattern2) 匹配 pattern1 或 pattern2 file@(.txt|.csv) 匹配 file.txt 或 file.csv
!(pattern) 匹配 不符合 pattern 的内容 !(*.bak) 匹配所有非 .bak 文件

POSIX字符类

POSIX标准预定义了一些常用的字符类,方便在处理字符串、文件名等情况时直接调用,在grep、sed、awk等Linux命令中,这些预定义的字符类需要在[ ]中使用,在tr等命令中则可以直接使用

通配符 匹配的范围 字符
[:alnum:] 匹配字母或数字 [a-zA-Z0-9]
[:alpha:] 匹配字母 [a-zA-Z]
[:digit:] 匹配数字 [0-9]
[:lower:] 匹配小写字母 [a-z]
[:upper:] 匹配大写字母 [A-Z]
[:space:] 空白字符 空格、\t、\n等
[:blank:] 空格和制表符 空格和\t
[:punct:] 标点符号 !@#$%^&*()等
[:graph:] 可见字符 非空白、非控制字符
[:print:] 可打印字符 包括空格在内的可打印字符
[:cntrl:] 控制字符 ASCII 0-31 和 127
[:xdigit:] 十六进制数字字符 [0-9A-Fa-f]
1.在ls等命令中匹配文件名 匹配file1.txt或fileaaa.txt等字母或数字结尾的文件 ls file[[:alnum:]].txt 匹配以字母开头的文件 ls [[:alpha:]]* 2.在grep命令中匹配字符 grep '[[:digit:]]' file.txt # 匹配数字 3.通过sed和awk处理字符串 sed 's/[[:space:]]//g' file # 删除空白字符 awk '/[[:alpha:]]/{print}' # 打印包含字母的行 4. 在Shell的[[ =~ ]]中进行正则匹配 if [[ "A" =~ [[:upper:]] ]]; then echo "大写字母"; fi 5. tr命令不需要嵌套[ ] echo "hello world" |tr '[:lower:]' '[:upper:]' #小写转大写

环境变量

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启动时,bash不会访问/etc/profile、$HOME/.bash_profile等文件,只会读取$HOME/.bashrc文件,而绝大部分Linux发行版$HOME/.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)来禁用特定脚本,或者修改脚本中的输出语句

tput 终端控制标准化接口

介绍

Terminfo数据库

Terminfo数据库是Unix/Linux 系统中用于描述终端能力的标准化数据库,为各种终端提供统一的描述方式,将终端功能映射为标准名称,使应用程序无需关心具体终端差异即可用统一接口(tput)控制终端,这种设计使得Unix/Linux系统能够支持数千种不同的终端类型

标准 Terminfo 数据库通常位于以下位置:

  • /usr/share/terminfo/
  • /lib/terminfo/

这些目录中可能包含上百种不同的终端配置,它们根据终端名首字母被整理到了1-9a-Z不同目录下,如:/usr/share/terminfo/x/目录下包含了xterm等常用X window相关终端的配置,每个终端类型在 Terminfo 数据库中都有一个独立的编译文件,该编译文件保存了终端支持的行数、列数、颜色、控制序列等终端属性

tput命令工具

tput是一个用于终端控制的工具,作为terminfo数据库的前端,它能根据terminfo数据库提供一种标准化的方式来查询和修改终端的行为,tput抽象了不同终端之间的差异,为开发者提供了统一的控制接口

调用tput命令时,tput会首先检查TERM环境变量来确定当前终端类型,然后从terminfo数据库中查找对应的终端配置。当执行tput命令时,它会将用户请求的能力名称(如setaf表示设置前景色)转换为该终端类型实际支持的控制序列,tput将转换后的控制序列输出到标准输出,这些序列会被终端解释并执行相应的操作

与CSI控制序列的区别

CSI控制序列是直接将控制字符包含在输出的字符串中,字节流不经任何处理直接交由终端处理,由终端内置解析器负责实时解释执行。CSI控制序列有以下特点:

  • 控制序列包含在输出序列中,简单高效,适合快速输出
  • 同一段控制序列,不同终端的解析可能不同,行为不一致,甚至可能出现因为终端不支持而出现乱码等情况。
  • CSI序列可能会被滥用于终端注入攻击

tput工作时,是先读取TERM环境变量确定终端类型,然后根据/usr/share/terminfo目录对应终端的配置编译文件,将终端控制请求转换为目标终端的专用序列,以达到动态翻译的效果。它有以下特点:

  • 控制序列动态生成,需要tput程序介入处理
  • 自动处理不同终端的实现差异,以保证行为一致,当某一个控制效果终端不支持时,tput会在序列生成阶段,回退到默认样式或终止操作,它能在跨平台脚本中提供更健壮的功能
  • 没有CSI注入风险

tput命令

tput [选项] [terminfo功能名称] [参数]

  • -T终端类型:手动指定一个终端类型用于查阅或操作,默认情况下tput会从TERM环境变量获取当前终端类型
  • -V 查看当前版本
  • -S >>标准输入:指定多个终端功能,此时需要从标准输入指定这些功能,而不再从命令行中指定,每行只能指定一个终端功能

基本terminfo功能

  • tput init 初始化终端,重新加载终端的配置信息,常用于终端设置被修改时,恢复终端的默认配置
  • tput clear 清空屏幕,将光标移动到左上角,在多数系统中,clear命令会在底层调用tput clear
  • tput reset 重置终端,清空屏幕,并且重置终端的光标位置、回显位置(echo、print等命令输出位置)、键盘映射等配置,常用于终端出现严重异常,恢复终端。在部分终端中,该命令会清空滚动缓冲区,即清空终端的历史命令输出内容
  • tput longname 输出终端的完整名称
1.查看当前终端的完整名称 tput longname xterm的输出: xterm terminal emulator (X Window System) 2.查看指定终端的信息,以AT&T 5620终端为例 tput -T5620 longname 输出: AT&T 5620 terminal 88 columns

颜色控制

常用终端类型xterm通常只支持8种(0-7颜色值)ANSI标准颜色(可以通过tput colors查看),如果需要使用更多颜色,可以修改终端类型为TERM=xterm-256color,以支持256种颜色

  • tput colors 查看当前终端支持的颜色数量
  • tput setaf 颜色值N设置终端前景色
  • tput setab 颜色值N设置终端背景色
  • tput op重置前景色和背景色为默认
  • tput sgr0重置所有文字样式和颜色为默认

部分常用颜色值

编号 颜色 编号 颜色
0 黑色 8 灰色
1 红色 9 亮红色
2 绿色 10 亮绿色
3 黄色 11 亮黄色
4 蓝色 12 亮蓝色
5 品红 13 亮品红色
6 青色 14 亮青色
7 白色 15 亮白色

文字样式控制

功能名 作用 功能名 作用
bold 加粗 dim 淡化
smul 开启下划线 rmul 关闭下划线
rev 反显(前后景色交换) blink 闪烁
smso 开启”突出模式” rmso 关闭”突出模式”
invis 隐藏文字 sgr0 重置所有样式和颜色
shell编程中设置文字加粗与下划线 BOLD=$(tput bold) UNDER=$(tput smul) RESET=$(tput sgr0) echo "${BOLD}${UNDER}加粗+下划线${RESET}"

光标控制

注意,指定光标(行列)位置时,从0开始计算位置

  • tput cols 获取终端列数(宽度)
  • tput lines 获取终端行数(高度)
  • tput home将光标移动到左上角(0,0)
  • tput cup 行 列将光标移到指定行列,行列从0开始计数
  • tput hpa 列将光标移动到指定列
  • tput vpa 行将光标移动到到指定行
  • tput ll移动光标到最后一行第一列
  • tput sc保存当前光标位置(部分终端可能不支持)
  • tput rc恢复保存的终端位置(部分终端可能不支持)
  • tput civis隐藏光标
  • tput cnorm恢复光标显示

模拟一个进度条

#!/bin/bash

#初始化终端
tput civis #隐藏光标
tput sc    #保存光标位置

#配置颜色
red_color=$(tput setaf 1)
blue_color=$(tput setaf 4)
reset=$(tput sgr0)

#模拟进度条
for i in {0..100};do

#计算进度条的"#"和空白部分,并缩减进度条长度到50字符
filled=$((i/2))     
empty=$((50-filled))

#下述命令printf "%0.s#" $(seq 1 $filled)中
#由于%0.s#部分限制参数输出宽度为0
#因此不会输出$(seq 1 $filled)的内容
#只会输出一个格式字符串中指定的"#"
#$(seq 1 $filled)会被展开
#命令解析为printf "%0.s#" 1 2 3...
#所有参数共用格式字符串"%0.s#",每有一个参数,就会输出一个"#"
#因此$(seq 1 $filled)输出内容并不重要,它们只负责让printf输出一个#
#$(seq 1 $filled)只用来指定重复输出#的个数
#如果输出数量一定,printf "%0.s#" {1..5}也是类似的作用
bar=$(printf "%0.s#" $(seq 1 $filled)) #输出#
space=$(printf "%0.s " $(seq 1 $empty))#输出空白符

tput rc  #每次都从原光标位置开始打印进度条,以覆盖上次的进度条
echo -ne "${blue_color}[${bar}${space}]${reset} ${red_color}${i}%${reset}"
sleep 0.05
done

#结束打印
echo       #输出换行
tput cnorm #恢复光标显示

文字自动居中

cols=$(tput cols)
msg="居中标题"
padding=$(( (cols - ${#msg}) / 2 ))
printf "%*s%s\n" "$padding" "" "$msg"

擦除行

  • tput ed 擦除光标之后的所有行,仅保留光标之前的行
  • tput el 擦除当前行光标之后到行末的内容
  • tput el1 擦除当前行光标之前到行开头间的内容
  • tput el2 擦除整行

插入/删除行与列

  • tput il 插入一行,重排 UI
  • tput dl 删除一行,重排 UI
  • tput ich 在当前位置插入字符
  • tput dch 删除当前位置字符

切换屏幕缓冲区

实现类似less、top、vim等命令,独占整个屏幕,且命令结束返回原屏幕的效果

  • tput smcup切换到新屏幕
  • tput rmcup切换回原屏幕
上一篇:Shell脚本语言
下一篇:Linux常用工具命令
z z z z z