【Linux基础】理解并善用Shell -- Shell精讲


一、Shell 概念向

百度百科-shell
wiki-shell

在计算机科学中,Shell俗称"壳"(之所以叫壳,是为了和计算机的"核"(内核)来区分),指 "为用户提供操作界面"的软件,有时也被称为命令解析器(command interpreter)。
进一步解释为:Shell是一个用户与内核的交互程序,它接收用户输入的指令,传递给内核执行(内核并不直接提供与用户的交互功能)
可以说,没有shell就无法使用Linux(对大部分使用者来说,做内核开发的不算-_-||),shell是Linux实打实的敲门砖

同时Shell 又是一种程序设计语言

  • 作为命令语言,它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令(shell脚本);
  • 作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的流程控制结构(循环和分支)

这两种含义,以前者为主,后者是建立在前者之上的。本文也主要介绍前者

另外,在排序算法中,Shell还是希尔排序(Shell’s Sort)的名称

Shell的分类

摘自百度百科:Shell

shell分为两大类:图形用户界面shell(GUI shell)和命令行式shell(CLI shell)。命令行shell提供一个命令行界面,而图形用户界面shell提供一个图形用户界面

一般提到的shell指的是命令行式的shell,如果不特别注明,shell都是指命令行式的shell

1. 图形界面shell

Graphical User Interface shell,即GUI shell

也就是日常使用的图形桌面。在图形界面Shell中,用户的指令表现为对鼠标和键盘的点击、拖拽等。

例如:

  • 应用最为广泛的 Windows Explorer (微软的Windows系列操作系统)、MacOS的GUI
  • 还有广为人知的Linux shell。包括BlackBoxFluxBox,以及功能更强大的CDE、GNOME、KDE、 Xfce 等

2. 命令行式shell

Command Line Interface shell,即CLI shell

一般提到的shell指的是命令行式的shell,如果不特别注明,shell都是指命令行式的shell

例如:

终端仿真器/模拟器

当使用图形界面时,我们需要另一个和 命令行shell 交互的叫做终端仿真器的程序。 如果我们浏览一下桌面菜单,可能会找到一个。虽然在菜单里它可能都被简单地称为 terminal,但是 KDE 用的是 konsole , 而 GNOME 则使用 gnome-terminal。 还有其他一些终端仿真器可供 Linux 使用,但基本上,它们都完成同样的事情, 让我们能访问 shell。
也许,你可能会因为附加的一系列花俏功能而喜欢上某个终端(仿真器)。

推荐一款目前使用感觉很好的终端模拟器:Tabby


交互式shell 和 非交互式shell(shell脚本)

交互式shell和非交互式shell、登录shell和非登录shell的区别

交互式模式:就是shell等待用户的输入,并且执行用户提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、退出。当用户退出后,shell也终止了。

非交互式模式:以shell脚本的方式执行。在这种模式下,shell不与用户进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。
非交互模式其实就是执行Shell脚本,Shell脚本=多个命令的组合。脚本不需要我们对其进行交互,它就只是很简单的将其脚本中的命令按顺序执行

登录式Shell 和 非登录式Shell

交互式shell和非交互式shell、登录shell和非登录shell的区别
linux之登录式shell和非登录式shell

登录式shell:需要用户名、密码登录后才能进入的shell (或者通过--login选项生成的shell),比如用xshell等终端模拟器连接服务器
运行形式:

  • 正常登录的shell,如物理机开机时进入的命令行界面、通过终端模拟器远程连接Linux服务器登录的shell
  • 执行bash -lbash --loginzsh -lzsh -login等类似命令进入的shell
  • 执行su - usernamesu -l usernamesu --login username进入的shell

非登录式shell:不需要输入用户名和密码即可打开的Shell。例如:在Xwindow(Mac、Gnome或KDE)中右击"打开终端(terminal)"窗口程序也是一个非登录式shell。
运行形式:

  • 图形界面下打开的终端窗口 (大多数Linux和其它基于X的系统都是;但MacOS从终端窗口进入的是登录式shell)
  • 执行bashzsh等类似命令进入的shell
  • 执行su username进入的shell
  • 自动执行的 shell 脚本 (即非交互式shell。除非脚本中有登录相关的代码,否则都是非登录式的)

su命令su意为switch user,是切换用户的命令
可以发现,su命令切换用户时,切换到的shell可以是登录式shell,也可以是非登录式shell
登录式shell的su命令,作用是在切换用户身份的同时,完整地切换工作环境,就好像是重新login为该使用者一样,当前使用的环境变量($HOME$SHELL$USER$PATH)也会切换成该使用者的,并且工作目录也会发生改变。我们知道,环境变量是用来定义操作系统环境的,因此如果系统环境没有随用户身份切换,很多命令无法正确执行

退出/注销命令

  • exit命令,用于退出一个shell(登录或非登录shell)
  • logout命令,用于退出登录shell(不能退出非登录shell)

了解登录式shell和非登录式shell的具体区别和用法,要先了解shell的启动/关闭文件(使用向内容,往下看)

shell发展史(各种shell)

shell有哪些?Zsh和Bash的区别是什么?
Shell、Bash、Zsh这都是啥啊
shell进化史

目前使用最广泛的是bash,实际上还有很多种shell
在这里插入图片描述
sh:sh全称为Bourne Shell,来自于1977年底的Unix v7版,由贝尔实验室的Steve Bourne开发,人们为了纪念他,就用他的名字命名了。sh是 UNIX 上的标准 shell,很多 UNIX 版本都配有 sh,二进制文件位置为/bin/sh,是第一个流行的 shell。

csh:sh 之后另一个广为流传的 shell 是由柏克莱大学的 Bill Joy 设计的,这个 shell 的语法有点类似C语言,所以才得名为 C shell ,简称为 csh。

tcsh:tcsh 是 csh 的增强版,加入了命令补全功能,提供了更加强大的语法支持。

ksh:ksh(Korn Shell) 集合了 csh 和 Bourne Shell 的优点并且和 Bourne Shell 完全兼容。

bash:bash(Bourne-Again Shell),是一个开源的GNU项目,他吸收了ksh和csh的一些特性,例如命令历史记录,命令行编辑,目录堆栈,很多实用的环境变量,命令行自动完成,等等。也发展了一些新的特性,如支持正则表达式和关联数组。
bash保持了对 sh shell 的兼容性,这意味着,针对 sh 编写的 shell 代码可以不加修改地在 bash 中运行。bash是各种 Linux 发行版默认配置的 shell

zsh:zsh对Bourne shell做出了大量改进,同时加入了Bash、ksh及tcsh的某些功能。比bash能好用很多,2019年起,macOS的默认Shell从Bash改为了zsh


二、Shell 使用向

查询和切换shell

查询命令

  • 查看当前使用的Shell:echo $SHELLecho $0chsh -l。还有更多方法:查看当前Linux使用的shell
  • 查看当前shell的PID:echo $$;定位当前shell的进程:ps -ef | grep $(echo $$)
  • 查看当前Linux的全部Shell:cat /etc/shells,会列出所有shell的二进制文件路径

切换命令:chsh -s [shell的二进制文件路径],如chsh -s /bin/bash。切换后要重新打开shell才能生效(当前窗口不生效)

Shell的启动&关闭文件(配置文件)

shell的配置文件用shell语法编写,并有明确的执行规则,配置文件中可以编写脚本、设置环境变量、设置别名、设置函数等

发现bash、zsh、ksh都墨守成规的共性有:

  • 系统级别的配置文件在/etc/目录下,对所有用户生效
  • 用户级别的配置文件在用户目录~/下,仅对对应用户生效
  • *profile后缀的配置文件(如.bash_profile.zsh_profile)都是登录式shell读取的,非登录式shell不读取
  • *rc后缀的配置文件(如.bashrc.zshrc):在zsh中即被非登录式shell读取也被登录式shell读取;在bash中被非登录式shell读取的,但登录式shell的文件中也包含了调用它的代码(显式读取)。所以最终,不论是登录式shell还是非登录式shell都会调用这类文件

了解这些,有助于更好的分类学习

至于具体的文件名、加载规则等,则要看具体shell的规则,这里以 最广泛使用的bash 和 最受欢迎的zsh 为例具体说明

bash

官方文档:Bash启动文件 下图为Chrome翻译,供大概了解
在这里插入图片描述

Difference Between .bashrc, .bash-profile, and .profile

交互式登录式shell加载规则(启动shell时从上到下的顺序读取)

  • /etc/profile:如果存在则执行该文件
  • ~/.bash_profile~/.bash_login~/.profile。按此顺序,找到第一个可读文件执行,一旦找到一个,后边的就不再执行了。每个单独的用户都可以拥有自己的一组这些文件。

    既然只加载一个,那为什么要有这三个文件呢?
    (摘自:bash的环境配置文件加载原理,Cameron Newham和Bill Rosenblatt在他们的著作《Learning the bash Shell, 2nd Edition》中解释了原因)
    .bash_login源自于csh的.login文件;.profile源自于Bourne shell 和 ksh的.profile文件
    bash这么做的优势就是:如果你在使用Bourne shell,你可以保留你的.profile。如果你需要添加针对bash的命令,你可以把它们放进.bash_profile文件,并且执行命令source ~/.profile。如果你想切换回Bourne shell,你不必修改你已存在文件。.bash_login和csh的.login也是相同的道理
    根本原因是为了兼容各种shell,但是由于shell的基础语法存在差异,这么做并不推荐

  • ~/.bash_logout:在注销登录时(执行exitlogout)执行

交互式非登录式shell加载规则(启动shell时从上到下的顺序读取)

  • /etc/bash.bashrc(仅在 Debian GNU/Linux、Ubuntu 和 Arch Linux 中可用):具体执行取决于每个发行版的策略
  • ~/.bashrc:如果存在则执行该文件

非交互式shell

  • $BASH_ENV:如果该变量有值,则将其值作为文件名读取和执行,相当于执行了以下命令
    if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi # .(点) 等同于 source 命令
    

交互式登录式shell显式调用~/.bashrc
可以看到,交互式非登录式shell相比于登录式shell,多执行了一个文件~/.bashrc。其实,为了避免登录式shell设置不完整而影响使用,都会让~/.bash_profile 调用 ~/.bashrc。结果,我们就会看到~/.bash_profile中都包含了以下代码,因此在每个交互式登录式 shell 中,.bashrc也会被执行
在这里插入图片描述

总结
这是一张很全面的bash启动文件加载流程图

图中的一些选项上面没有提到,都是bash命令的选项(bash文档-调用bash),并不常用
非交互式 -> NO -> --login的方式,是通过类似bash --login xx.sh调用;或shell脚本的解释符写为类似#!/bin/bash --login,然后直接用xx.sh全路径调用脚本(如执行~/test.sh)
在这里插入图片描述

不同发行版内置bash的变化

不同的Linux发行版中携带的shell和官方shell可能会稍有不同

eg1. CentOS的/etc/profile.d/
linux发行版本中centos预设了很多方便的alias命令别名,这就是为什么很多人感觉centos系列使用上更加顺手的原因之一。
centos7的/etc/profile中包含了以下代码。即在登录式shell执行etc/profile时,还会执行/etc/profile.d/下的*.sh脚本文件。最后的d表示directory

for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then 
            . "$i"
        else
            . "$i" >/dev/null
        fi
    fi
done

其中ll命令别名就定义在/etc/profile.d/colorls.sh

这种 用文件划分配置 的管理方式看起来很方便,当要修改某个配置时,可以在/etc/profile.d/目录下快速找到对应的配置文件,而不是在/etc/profile中翻找某一片对应的代码

eg2. centos的/etc/bashrc
centos的~/.bashrc中有如下代码,表明要读取/etc/bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
	. /etc/bashrc
fi

/etc/bashrc的开头这么写道,表示这里适合放置系统级的函数和别名
在这里插入图片描述

eg3. archlinux的/etc/bash.bash_logout/etc/bash.bashrc
archlinux内置的bash(archlinux-bash文档),比bash官方版多了/etc/bash.bash_logout/etc/bash.bashrc两个文件,是对官方版的一种补充
在这里插入图片描述

zsh

官方文档:zsh文件:启动/关闭文件 下图为Chrome翻译,供大概了解
在这里插入图片描述

交互式登录式shell加载顺序(根据官方文档结合实操整理) (如果设置了$ZDOTDIR,则替换默认的~/目录)

  • /etc/zshenv -> ~/.zshenv -> /etc/zprofile -> ~/.zprofile -> /etc/zshrc -> ~/.zshrc -> /etc/zlogin -> ~/.zlogin
  • ~/.zlogout -> /etc/zlogout:注销登录时(执行exitlogout)执行

交互式非登录式shell加载顺序(根据官方文档结合实操整理) (如果设置了$ZDOTDIR,则替换默认的~/目录)

  • /etc/zshenv -> ~/.zshenv -> /etc/zshrc -> ~/.zshrc
    速记技巧:交互式非登录式shell不执行rc后缀的文件

实操
shell的配置文件并不是所有都会被默认创建,我手动补全了文件,并在每个文件中都加入了特征打印,实测如下
在这里插入图片描述

不同发行版内置zsh的变化

不同的Linux发行版中携带的shell和官方shell可能会稍有不同

eg1:archlinux携带的zsh,对配置文件的所在目录进行了稍微调整,如下图
(archlinux-zsh文档)
在这里插入图片描述

*profile文件和*rc文件的区别和用法

参考1:Difference Between .bashrc, .bash-profile, and .profile

参考2:在centos中可以看到,~/.bash_profile被推荐用于设置环境和启动程序,~/.bashrc被推荐用于设置别名和函数
(同样,/etc/*rc用来配置系统级的函数或别名,/etc/*profile用来配置系统级别的环境变量)
在这里插入图片描述

先说结论:

  • *profile文件用于设置环境和启动程序
  • *rc文件用于设置别名和函数

    顺便一提,在 UNIX 世界,rc是英文run command的缩写,这一缩写据说源于 1965 年 MIT CTSS 的 runcom 命令。它表示文件中存放需要执行的命令,经常被用作程序之启动脚本的文件名

补充两个知识点:

  1. 第一个shell总是登录式的 (可见后节-系统启动时的shell加载行为)
    • MacOS的终端窗口进入的是登录式shell
    • 虽然Linux图形界面(如Gnome)的终端窗口进入的是非登录式shell,但在登入图形界面时会加载一次登录式shell文件
  2. 环境变量会被子shell继承、别名和函数不会被继承(详见后节-环境变量、别名、函数)

然后结合本文已经讲过的:*profile配置文件被登录式shell执行,*rc配置文件被登录式和非登录式shell执行

所以:

  • 之所以把环境变量放在*profile文件中,是因为登录式shell能获取到它,之后打开的非登录式shell也能够继承到它(而不用再加载一次)
  • 之所以把别名、函数放在*rc文件中,是因为它没有继承性,这么做即能让登录式shell加载到,也能让非登录式shell也能加载到

使用中的一些细节

  1. shell的那些配置文件有些不是默认就被创建好的,可能需要在用的时候手动创建

  2. 当切换shell时(比如bash切换到zsh),我们或许会想让zsh读取bash的~/.bash_profile,这时可以把内容复制到zsh的配置文件,但最方便的方式是在zsh的配置文件里添加一行source ~/.bash_profile (source命令用于加载配置文件)

  3. 环境变量修改后要重新进入shell,或者执行source <filepath>命令重新加载


环境变量、别名、函数


环境变量、别名、函数 都是Shell中的概念,在Shell的配置文件或命令行中定义和使用

环境变量、别名、函数 分为系统(全局)级、用户级、会话级

  • 系统级 定义在/etc/目录下的配置文件中,对所有用户都有效。
  • 用户级 定义在用户目录(~/)下的配置文件中,仅对对应的用户有效。
  • 会话级 定义在当前shell进程,退出则失效。

继承
环境变量可以被子进程继承:环境变量具有全局性,每一个子进程都会继承父进程的环境变量 (【Linux】进程概念 —— 环境变量)
父shell和子shell就是父子进程的关系,所以每个shell都会从创建(运行)它的 父shell 继承环境变量。所以我们 不必担心 “非交互式shell不加载启动文件所以会获取不到系统变量” 这种问题

别名、函数 不会被子进程继承

实测如下,在~/.zprofile中设置变量、别名、函数,MacOS打开终端窗口时会加载这个文件,所以能够获取到$HA、ll、oo。进入子shell不会加载这个文件,此时能获取到$HA(从上级shell继承过来了),但获取不到ll、oo(没有继承过来)
在这里插入图片描述

开机自启程序读不到环境变量?

通常,我们会用/etc/rc.d/rc.local文件或systemd 设置开机自启程序
(CentOS中,/etc/rc.d/rc.local 会拥有一个/etc/rc.local的软链接文件)
(systemd提供的管理命令是systemctl)

ps:rc.local已经过时了,虽然它是大部分Linux发行版的通用文件,但这是为 systemV 系统的兼容性而保留的 (How to use /etc/rc.local at boot)。目前主流的Linux发行版主要用systemd控制开机启动
centos7中也提到了该文件的过时
在这里插入图片描述

这两种自启方式都有同一个问题:自启程序读取不到Shell环境变量

这和Linux的启动流程有关:Linux启动流程及systemd服务详解特性介绍 | Linux 操作系统启动流程
rc.localsystemd都是在系统就绪(完全启动)之前就加载自启程序,这时还没有启动shell(用户登入时才启动shell),也就没有加载shell的配置文件,所以自启程序读不到shell环境变量。

可以发现上图rc.local文件最后也写道:that this script will be executed during boot

rc.local解决方法
rc.local中用export声明所需变量,如export JAVA_HOME=usr/local/java/

不必担心这个变量会干扰到shell,实测shell中读取不到这里定义的变量。可能是因为他们并没有父子进程关系,所以变量没有被shell继承

测试方法,在rc.local中加入以下代码

export RTEST=o.o
echo rclocal $(date) >> /home/wushu/test.txt # 注意不能写为 ~/test.txt 因为引导期间不知道`~`是什么

启动后先去test.txt文件查看输出,以确保该文件已执行。然后打开终端窗口执行echo $RTEST,获取不到值,说明变量没有被shell继承

网上也有说用source加载shell配置文件的,如source /etc/profile。但我不建议这种做法,因为shell配置文件里的东西并不都是自启程序需要的。

systemd解决方法
systemd的管理单位是服务(service),即便是要实现一个自启脚本也需要创建成服务。服务声明在/usr/lib/systemd/system/xxx.service中,xxx就是服务名

systemd提供了配置指令:EnvironmentEnvironmentFile 用于设置变量。只需要在对应的xxx.service中用该指令设置即可
(详见:在 systemd 单元中使用环境变量)
Environment="JAVA_HOME=/usr/local/java/"


Shell命令分类(内建、外部)

Shell可识别命令和Shell脚本,其中命令分为 外部命令 和 内建命令
bash内建命令文档
zsh内建命令文档

外部命令大多在usr/bin/usr/local/bin等目录下,以可执行文件的形式存在。这些目录应当设置在shell的PATH变量中,以便我们在shell中调用时,shell能够找到它们。

内建命令与shell本身编译成一体,所以内建命令不会有很多,只有那些最常用的命令才有理由成为内置命令,比如cdkillechoaliassource等。过多的内建命令会导致 Shell 程序本身体积膨胀,运行 Shell 程序后就会占用更多的内存。Shell 是一个常驻内存的程序,占用过多内存会影响其它的程序。
每个shell都有自己的内建命令集,它们不完全一致。但为了提供兼容性,它们大多都会包含不少sh、bash中的内置命令

冷知识:其实shell的一些语法也是用命令实现的。比如:ifwhileforswitchcase
详见:zsh文档-组合命令bash文档-复合命令

通常来说,内建命令会比外部命令执行得更快,执行外部命令时不但会触发磁盘 I/O ,还需要 fork 出一个单独的进程来执行,执行完成后再退出。而执行内建命令相当于调用当前 Shell 进程的一个函数。

type命令
我们可以使用type命令来区分命令是内建的还是外部的。Linux命令之查看命令类型type

有些命令有多种实现。例如echo和pwd既有内建命令也有外部命令,两种实现略有不同。shell默认调用内建命令,如果想要使用其外部命令实现,直接指明对应的文件路径就可以了

wushu@wushudeMacBook-Pro ~ % type echo
echo is a shell builtin	# echo是内建命令
wushu@wushudeMacBook-Pro ~ % type ls
ls is /bin/ls	# ls是外部命令
wushu@wushudeMacBook-Pro ~ % type -a echo
echo is a shell builtin 
echo is /bin/echo # 原来echo还有外部命令
wushu@wushudeMacBook-Pro ~ % type type
type is a shell builtin # type也是内建命令
wushu@wushudeMacBook-Pro ~ % type -t ls
type: bad option: -t # 怎么不能用-t选项?
wushu@wushudeMacBook-Pro ~ % type -a type
type is a shell builtin
type is /usr/bin/type # 原来type也有个外部命令实现
wushu@wushudeMacBook-Pro ~ % /usr/bin/type -t ls # 调用type的外部命令实现
file # 外部type可以用-t选项

查看文档的正确姿势

使用linux的过程中,通常会用到这三种文档:Shell文档、命令文档、应用文档

如果是做Linux嵌入式、驱动、内核开发,可能还需要看其它对应的文档吧,这些文档我也不清楚叫什么 (或许就叫 Linux嵌入式/驱动/内核开发文档)

这里的应用是指redis、docker这种应用

顺便一提(不知道这段写在哪),GNU发布的软件都可以在其官网找到文档 - GNU在线手册,如 BashGzipwget

Shell文档

shell文档介绍如何使用Shell,如shell配置文件、编写脚本的语法、内置功能(如tcp日历ftp)

本文编写过程中就大量参考了Shell文档,如:bash官方文档zsh官方文档archlinux-zsh文档

命令文档

命令文档介绍如何使用某个命令:命令的格式、可用的选项和作用等

查看命令文档的方式主要有:man命令、--help选项、info命令、help命令(bash内建命令)、面向百度(o.o屡试不爽)

man命令(Linux手册页)
man,意为manual。man命令是Linux下的帮助命令,用于查看Linux手册页(man page)中的内容

手册页是Unix发展的产物,手册页文件存放在/usr/share/man目录下,分为8个章节

章节内容翻译
1General commands一般命令
2System calls系统调用
3Library functions, covering in particular the C standard library库函数,尤其是C标准库
4Special files (usually devices, those found in /dev) and drivers特殊文件(通常是在/dev中找到的设备)和驱动程序
5File formats and conventions文件格式和规范
6Games and screensavers游戏和屏保
7Miscellaneous杂项
8System administration commands and daemons系统管理命令和守护进程

man命令man <command>即可查看对应命令的手册内容,man -w <command> 能够查询该命令手册的路径

使用man命令时,手册页通常是用less打开的,所以 less 命令的键盘快捷键在 man 中也可以使用,如用方向键上下移动,按 q 退出查看手册页

下图中,左上角的LS表示手册名称,而(1)表示该手册位于第一章节,第一个章节存放的就是各种各样命令的手册文件,所以大多数命令手册都在第一章。
在这里插入图片描述

ps:如何为你的程序创建手册页
How to Create a man Page on Linux
How To – Linux / UNIX Create a Manpage

–help选项
--help是命令自带的选项,是GNU的编码标准之一,所以很多命令都具备该选项

GNU Coding Standards
在这里插入图片描述

info命令
info是GNU(GNU-info)出的超文本帮助系统,用来阅读info格式的文档

类似于man,info的文档存放在/usr/share/info目录中

info 页面结构化程度更高,比 man page 编写得要更好、更容易理解,支持的富文本格式更多。而man手册仅支持极少的富文本,目测只有标题、加粗、缩进、斜体、下划线

但 man page 阅读起来更加方便。一个 man 手册只有一级标题,而 info 页面将内容组织成多级标题,每个标题称为节点(node),每个标题下可能存在子标题(称为子节点)。要理解 info 命令,不仅要学习如何在单个节点中浏览,还要学习如何在节点和子节点之间切换。

目前使用最多的还是man,所以info命令找不到info格式的文档时,会显示manpage的内容(实测表现是这样)

[wushu@centos7]~% info -w dir
/usr/share/info/coreutils.info.gz
[wushu@centos7]~% info -w tar 
/usr/share/info/tar.info.gz
[wushu@centos7]~% info -w ifconfig
*manpages* # 如果`/usr/share/info`中没有,就会去找man page
[wushu@centos7]~% info -w cd 
*manpages*

help命令
help是bash提供的内建命令,仅用于显示bash内建命令的帮助信息

应用文档

以redis来举例

redis是一个key-value数据库软件,提供了redis-cliredis-serverredis-benchmark命令用于管理和使用
但作为一个大型软件,如果单纯依靠阅读这几个命令的文档,基本就会像读天书,摸不到头脑。

因为命令文档只是单纯用来介绍这个命令的用法,有什么选项。它虽然能直观的告诉你这个命令如何使用,但如果你不了解redis,就不知道执行这个命令之后你还能干些什么、还要做些什么。比如你用redis-server启动了redis,然后呢?你不知道接下来要干啥,不知道它可以用来存储数据,也不知道可以用客户端连接它

而应用文档则会图文并茂地介绍整个软件的所有功能用法,比如如何配置、连接、做分布式等,这些不仅需要用到命令,还要编写配置文件、设置防火墙等操作

所以,一些小的工具软件通常只查看一下命令文档,知道命令格式就可以用了,但一些大型应用软件还是要先好好阅读官方文档才能更好地掌握和使用


三、进阶

系统启动时的Shell加载行为(为什么MacOS的终端进入的是登录式shell?)

Why are interactive shells on OSX login shells by default?
在这里插入图片描述

CentOS(图形界面)的做法 【上边参考图已经表示基于X的系统都是这样的。我只测试了CentOS7+Gnome(自带)+bash(自带)/zsh(另外安装)】

在CentOS7中,终端窗口打开的是非登录式shell,这就会出现一个问题:(假设用的bash)在~/.bash_profile中设置一个环境变量后,每次打开终端窗口都需要执行一次source ~/.bash_profile才能加载到这个变量,这无疑非常麻烦

好在(经过测试),CentOS每次登入Gnome(开机or注销重新登陆),都会加载一次登录式shell,于是之后从终端窗口打开的shell就会继承这些变量(Gnome为父进程,打开终端窗口为子进程,子进程会继承父进程的环境变量)。所以每次修改了变量,只需要重新登入就好,相对没那么麻烦

测试方法
bash:在~/.bash_profile中加入echo "~/.bash_profile" $(date) >> ~/test.txt
zsh:在~/.zprofile中加入echo "~/.zprofile" $(date) >> ~/test.txt
每次登入Gnome,text.txt中就会被写入此刻的时间,从而推断出该行为
ps:我不知道这种行为到底是和Linux发行版内核有关,还是和他们使用的图形界面有关,还是和X有关。即便我们不知道,也不影响使用。但如果你知道,希望能告诉我(thanks!)

并且,通常我们都不会把CentOS用于办公,而是作为服务器。通过ssh连接服务器时,进入的必定是登录式shell,也就避免了这种麻烦

MacOS的做法 【macOS Monterey v12.6 intel-chip】
我们已经知道了CentOS处理上的缺陷,MacOS非常简单有效地解决了它:MacOS每次打开终端窗口都是一个登录式shell

另外,因为MacOS不是基于X的系统,所以它并没有在登入系统时加载登录式shell。
所以要注意一个问题:打开终端窗口进入登录式shell只是"MacOS终端窗口"软件的行为,如果你用其它的终端模拟器连接本机shell,可能默认进入的是非登录式shell,这时就需要调整为登录式shell

iTerm2设置位置
在这里插入图片描述
tabby设置位置
在这里插入图片描述

为什么CentOS不采用MacOS的做法?
MacOS的这种做法虽然好,但为什么CentOS不改成这样呢?

我认为可以在CentOS的~/.bash_profile文件里找到答案,这里写到:把环境和启动程序放在这里
在这里插入图片描述

所以在CentOS中,我们可以在~/.bash_profile里添加sh abc.sh,这样在每次登入桌面后就会执行abc.sh。但在MacOS中,这样显然就不实用了,因为每次打开终端窗口都会执行abc.sh,这就起不到"把启动程序放在这里"的效果

当然,MacOS作为使用最广泛的桌面系统之一,如果有这种 开机自启 的需求,是有其它方法的,在这里就不赘述了

所以,归根结底,这只是系统设计者的一种偏好而已

Unix可执行文件的解释器指令 #! (执行shell脚本的正确方式)

wiki-shebang

#!,英文为shebang(/ʃɪˈbæŋ/),是源自于Unix的脚本标准。当一个带有shebang的文本文件在Unix|的操作系统中被当做可执行文件使用时程序加载器机制会将文本初始行的其余部分解析为一个解释器指令。加载器执行指定的解释器程序,使用试图运行脚本时最初使用的路径作为参数传递给它,以便程序可以使用文件作为输入数据(参数)。

#!语法为#! 解释器绝对路径[可选参数]#!后的空格是可选的

例如,有一个可执行的js文件~/xx.js,它的首行为#!/usr/bin/env node,那么程序加载器会被指示使用/usr/bin/env node运行程序,并将~/xx.js作为第一个参数,即运行/usr/bin/env node ~/xx.js。在Linux中,这种行为是内核空间和用户空间代码的结果

env命令会在$PATH中查找后边的指令(node),解决了不同用户指令路径不同的问题。~/xx.js最终会被node解释运行

执行shell脚本的正确方式

例如,有一个可执行的shell脚本~/script.sh,它的首行为#!/bin/bash -l,那么程序加载器会被指示使用/bin/bash -l运行程序,并将~/script.sh作为第一个参数,即运行/bin/bash -l ~/script.sh

正确执行shell脚本的方式是把脚本作为一个可执行文件,上述例子中,运行脚本的命令是直接执行~/script.sh。只有这样,程序加载器才会读取shebang指令

如果我们显式使用解释器运行脚本,如bash ~/script.shzsh ~/script.sh,shebang指令是不起作用的,脚本会被显式指定的解释器运行。
解释器运行脚本时,通常会忽略shebang行,因为#字符在许多脚本语言中是一个注释标记


四、实操

判断登录式/非登录式shell的方法

方法一:echo $0

有一种判断 login shell vs non-login shell 的快速的方法,使用命令 echo $0

bash文档中明确提到了该用法:A login shell is one whose first character of argument zero is ‘-’, or one invoked with the --login option.
zsh-前置命令修饰符提到了用-前置修饰命令,-就会作为argv[0]的前缀

[root@centos7 ~]# echo $0
-bash # 以 - 开头,则是登录式shell
[root@centos7 ~]# bash  # bash命令,看似没什么变化,其实会进入一个非登录式的子shell
[root@centos7 ~]# echo $0
bash # 不以 - 开头,则是非登录式shell
[root@centos7 ~]# exit
exit # 退出子shell
[root@centos7 ~]# echo $0
-bash

但这也有不好使的时候,如果用 shell命令的完整路径 打开shell,echo $0就会显示shell命令的路径,不会出现-符号
就像这样:

wushudeMacBook-Pro:~ wushu$ /bin/bash -l
i am .bash_profile
wushudeMacBook-Pro:~ wushu$ echo $0
/bin/bash

一些终端模拟器(如tabby)就是用这种命令方式启动shell的,这就无法区分登录式还是非登录式了

方法二:shopt命令

shopt是一个内置命令,用来查看shell行为特性的变量
输入 shopt -p login_shell,如果显示-s(意为set)表示该选项状态为on,如果显示-u(意为unset)表示该选项状态为off

wushudeMacBook-Pro:~ wushu$ shopt -p login_shell
shopt -s login_shell

方法三:配置文件打印特征

在 登录式shell启动文件和非登录式shell启动文件中分别添加一段特征打印

如 在~/.zprofile中添加 echo i am .zprofile;在.zshrc中添加echo i am .zshrc
这样,当我们打开终端、连接shell的时,就会打印出特征信息(因为shell要执行启动文件)
登录式shell会打印i am .zprofile,反之不会,以此来判断是否是登录式shell
在这里插入图片描述

shell命令提示符的配置

命令提示符的显示格式和颜色,属于shell的配置,不同shell的配置方式不一样

bash

bash文档-Controlling the Prompt
如何修改Bash Shell的提示符的格式和配色

在配置文件中加入以下代码

export PS1='\e[36m\u@\h \e[34m\w\e[0m \e[36m\$\e[0m '

格式代表符

  • \u 用户名
  • \h 主机名
  • \w 所在目录
  • \$ 用户角色标识,root用户显示#,其它用户显示$`

颜色(ANSI);x具有的值有:0、1(粗体)、4(下划线),分号用来分隔数字,如果是0;可以省略不写

  • 重置为默认 : \e[0m
  • 黑色 : \e[x;30m
  • 红色 : \e[x;31m
  • 绿色 : \e[x;32m
  • 黄色 : \e[x;33m
  • 蓝色 : \e[x;34m
  • 洋红 : \e[x;35m
  • 青色 : \e[x;36m
  • 白色 : \e[x;37m

zsh

zsh文档-Prompt Expansion
zsh文档-Prompts
Zsh官方文档-提示(Chrome翻译)
自定义 zsh 提示(配置PROMPT或PS1)
让MacOS的终端靓起来,给MacOS终端CLI添加颜色

在配置文件中加入以下代码

autoload -U colors && colors # 如果不写这行,提示符颜色不生效
# 配置提示符的格式和颜色,变量名可以是PS1、PROMPT、prompt的任意一个(这些名字源自不同的shell)
# 不配置的时候,它们的值默认是`%n@%m %1~ %# `
export PS1="%{$fg_bold[cyan]%}%n%{$reset_color%}@%{$fg_bold[cyan]%}%m %{$fg_bold[green]%}%1~ %{$reset_color%}%# "
# 另一种格式
# export PS1="%F{243}%n@%m%f %F{75}%1~ %F{243}%#%f "
# 一种很酷的格式
# export prompt="%F{243}┌─ [ %1~ ]%n@%m%f  %F{243}%# [%*]
# └─○ %f"

zsh的显示格式默认是:[用户名]@[主机名] [当前路径] %,对应的值为%n@%m %1~ %#

  • %n 是当前用户名
  • %m 是当前主机名的第一元素
  • %1~ 是当前目录,不过会自动将用户目录替换为~
  • %# 是用户角色标识,root用户显示#,其它用户显示%
  • 更多转义符见官方文档

颜色定义方式有两种

  1. %{$fg_bold[cyan]%}
    $fg$bg表示前景和背景,$bg_bold$fg_bold表示粗体,$bg_no_bold$fg_no_bold切换回非粗体
    []内写标准的ANSI颜色名,如black,red,green,yellow,blue,magenta,cyan,white
  2. %F{color}
    color是256色的颜色值,也可以使用标准ANSI颜色名;%f恢复默认颜色,%B粗体,%b恢复为非粗体
    在这里插入图片描述

如果最后不恢复默认颜色,敲出来的命令也会用前面设置的颜色显示

修改ls命令输出的文件颜色

让MacOS的终端靓起来,给MacOS终端CLI添加颜色

查看ls命令的man页面,可以看到ls使用某些变量来控制输出的文件颜色。(centos的man ls没有写这段功能)
(这个黄底的man页面,是mac提供的功能,选中命令后右击点击打开man页面。更方便阅读)
在这里插入图片描述
在shell配置文件中定义这两个变量

export CLICOLOR=1       # 开启文件配色
export LSCOLORS=ExGxFxdaCxDaDahbadeche # 赋值颜色。
# LSCOLORS 每组2个字母,代表两个颜色,第一个字母为前景色,第二个字母为背景色。
# 共11组,每一组代表一种文件类型,而字母本身是颜色的意思

11组文件类型的意思如下

1. directory 
2. symbolic link 
3. socket 
4. pipe 
5. executable (可执行文件,x权限) 
6. block special 
7. character special 
8. executable with setuid bit set (setuid=Set User ID,属主身份) 
9. executable without setgid bit set 
10. directory writable to others, with sticky bit 
11. directory writable to others, without sticky bit

LSCOLORS中,各个字母代表的颜色如下,注意大小写是有区别的
这些颜色是标准的ANSI颜色,实际显示可能有所不同,取决于所使用的终端的ANSI颜色配置

a 黑色 
b 红色		代表压缩文件或者压缩包
c 绿色		代表可执行文件
d 棕色		代表块文件
e 蓝色		代表目录
f 洋红色 
g 青色		代表链接
h 浅灰色 
A 黑色粗体 
B 红色粗体 
C 绿色粗体 
D 棕色粗体 
E 蓝色粗体 
F 洋红色粗体 
G 青色粗体 
H 浅灰色粗体 
x 系统默认颜色 

如果配置之后,觉得某个颜色不是很满意,可以调整终端/shell模拟器的ANSI颜色
在这里插入图片描述

zsh配置tab补全忽略大小写

在配置文件中加入以下代码

# 按tab补全时不区分大小写
autoload -Uz compinit && compinit -u
zstyle ':completion:*' matcher-list 'm:{[:lower:][:upper:]}={[:upper:][:lower:]}' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]} l:|=* r:|=*' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]} l:|=* r:|=*' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]} l:|=* r:|=*'

CLI Shell和图形界面的比对理解

已经讲过,平时频繁使用的电脑图形界面就是shell的一种(图形界面式shell),只不过一般提到的shell都是命令行式shell。
(以下不明确指出的shell都表示命令行式shell)
所以通过 图形界面 和 shell 的一些比对 就能让我们彻底地理解shell

  • 执行命令 = 鼠标和键盘的点击、拖拽等

  • 登录Shell = 打开windows并登陆账号。登陆之后才算进入了系统,才能做我们想做的事

  • shell环境变量 = windows的环境变量。也分为系统(全局)变量和用户变量,并且作用范围也是一样的(全局变量对每个用户都生效,用户变量只对对应用户生效)

  • 更换shell(bash、zsh等) = 更换图形界面
    在Linux上不管是 命令行式shell 还是 图形界面式Shell 都是可以更换的。比如常见的比较流行的Gnome、KDE等图形界面,都可以在大多数Linux发行版中安装使用,同一个Linux系统可以安装和更换不同的图形界面。
    不管是换图形界面还是换shell,其实都只是换了个和系统交互的上层软件而已

  • MacOS中的automator(自动操作) = shell脚本。automator通过鼠标键盘的点击拖拽等将多种操作(即指令)进行组合,其实就是"无代码"的脚本程序。所以自动操作=shell脚本=多个指令的组合

  • yum、apt、brew,就是应用商店(360软件商店等)

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Shell脚本编程是一项非常实用的技能,能够帮助我们自动化很多日常操作,提高工作效率。以下是一些关于Shell脚本编程的技巧: 1. 善用变量:在Shell脚本中,变量能够帮助我们简化代码,提高可读性和可维护性,同时也能够提高脚本的灵活性。在使用变量时需要注意命名规范,勿使用系统保留关键字。 2. 正确处理命令行参数:在Shell脚本中,传递命令行参数是非常普遍的。处理命令行参数时,需要对参数进行校验和处理,避免可能的错误。可以使用getopts或者shift命令来处理命令行参数。 3. 控制流操作:控制流操作在Shell脚本编程中扮演着重要的角色。可以使用if、for、while等关键字对程序进行控制。 4. 使用函数:函数是组织代码、提高可复用性的重要手段。在Shell脚本中,定义函数需要注意参数传递和返回值处理。 5. 错误处理:错误处理对于Shell脚本编程尤为重要。可以在代码中通过trap命令、bash -e等方式对错误进行捕获并进行处理,使程序更加健壮和安全。 6. 善用管道和重定向:Shell脚本中的管道和重定向功能非常强大,可以让我们实现很多高级操作。可以使用“|”符号将多个命令进行组合,也可以使用重定向符号“>”、“>>”等将输出结果保存到文件中。 以上是一些关于Shell脚本编程的技巧。当然,实际编程中需要注意代码的可读性、可维护性和易用性,将优秀的编程习惯应用到Shell脚本编程中,才能写出质量高、易于维护的脚本代码。 ### 回答2: shell脚本编程技巧主要包括以下方面: 1. 变量的使用 在shell脚本编程中,变量是非常重要的,可以用于存储和操作数据。在使用变量时,应该注意命名规范,使用大写字母来表示常量,而使用小写字母来表示变量。此外,还应该注意变量的作用域问题,尽量使用局部变量,使程序结构更加清晰易懂。 2. 条件语句 条件语句是shell脚本中用于判断某个条件是否成立的关键词,常用的条件语句包括if、elif和else。在使用条件语句时,应该注意条件的书写方式,尽量使用简单易懂的语句。 3. 循环语句 循环语句是shell脚本中用于重复执行某个指令的关键词,常用的循环语句包括for和while。在使用循环语句时,应该注意循环的顺序、循环的范围和循环的结束条件。 4. 函数的使用 函数是shell脚本中用于封装一些常用代码的关键词,可以节省代码的重复编写。在使用函数时,应该注意函数的参数传递、返回值的处理和函数的命名规范。 5. 错误处理 在编写shell脚本时,必须考虑到错误的处理,比如输入错误的参数、文件不存在等问题。错误处理的方式可以使用if语句和return语句等方式,尽量提供清晰明了的错误提示信息。 总之,在编写shell脚本时,应该遵循良好的编码习惯,注重程序的可读性和可维护性,同时也应该不断学习和积累经验,提高自己的编程技巧。 ### 回答3: Shell脚本编程技巧是在Linux系统中实现自动化操作和任务的关键。以下是一些Shell脚本编程技巧: 1. 调试技巧:在Shell脚本中调试非常重要,可以使用set-x选项打开跟踪功能,以便检查脚本中出现的问题。 2. 错误处理技巧:在Shell脚本中,应该考虑在整个脚本中添加错误处理功能,以便可以在发生错误时采取适当的措施。 3. 命令参数技巧:Shell脚本可以从命令行参数中读取输入,可以使用$符号从命令行读取参数。 4. 文件处理技巧:Shell脚本中可以使用文件系统函数,如cat、echo、grep、sed、awk等,以对文件进行各种操作。 5. 字符串处理技巧:Shell脚本可以使用字符串处理函数,如${#str},${str:0:5}等,以进行常见的字符串操作。 6. 数组技巧:在Shell脚本中,可以使用数组来存储和处理多个值。 7. 正则表达式技巧:正则表达式是Shell脚本编程的一个重要组成部分,可以在Shell中使用多种正则表达式操作,如grep、awk等。 总之,熟悉这些Shell脚本编程技巧是非常有用的,在Linux系统和其他Unix操作系统中实现自动化操作和任务方面非常方便。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值