• Shell脚本:Linux Shell脚本学习指南(第一部分Shell基础)二


    第一部分:Shell基础(开胃菜)二

    八、Linux Shell命令提示符

    启动Linux桌面环境自带的终端模拟包,或者从 Linux 控制台登录后,便可以看到 Shell 命令提示符。看见命令提示符就意味着可以输入命令了。命令提示符不是命令的一部分,它只是起到一个提示作用。

    不同的 Linux 发行版使用的提示符格式大同小异,例如在 CentOS 中,默认的提示符类似下面这样:

    [mozhiyan@localhost ~]$

    各个部分的含义如下:

    • []是提示符的分隔符号,没有特殊含义。
    • mozhiyan表示当前登录的用户,我现在使用的是 mozhiyan 用户登录。
    • @是分隔符号,没有特殊含义。
    • localhost表示当前系统的简写主机名(完整主机名是 localhost.localdomain)。
    • ~代表用户当前所在的目录为主目录(home 目录)。如果用户当前位于主目录下的 bin 目录中,那么这里显示的就是bin
    • $是命令提示符。Linux 用这个符号标识登录的用户权限等级:如果是超级用户(root 用户),提示符就是#;如果是普通用户,提示符就是$

    总结起来,Linux Shell 默认的命令提示符的格式为:

    [username@host directory]$

    或者

    [username@host directory]#

    什么是主目录?

    Linux 系统是纯字符界面,用户登录后,要有一个初始登录的位置,这个初始登录位置就称为用户的主目录(home 目录)。超级用户的主目录为/root/,普通用户的主目录为/home/用户名/

    有的资料也称为“家目录”,“家”是 home 的直译,它们都是一个意思。

    用户在自己的主目录中拥有完整权限,所以我们也建议操作实验可以放在主目录中进行。

    我们使用 cd 命令切换一下用户所在目录,看看有什么效果。

    [mozhiyan@localhost ~]$ cd demo

    [mozhiyan@localhost demo]$ cd /usr/local

    [mozhiyan@localhost local]$

    仔细看,如果切换用户所在目录,那么命令提示符中会变成用户当前所在目录的最后一个目录(不显示完整的所在目录 /usr/ local/,只显示最后一个目录 local)。

    第二层命令提示符

    有些命令不能在一行内输入完成,需要换行,这个时候就会看到第二层命令提示符。第二层命令提示符默认为>,请看下面的例子:

    [mozhiyan@localhost ~]$ echo "Shell教程"

    Shell教程

    [mozhiyan@localhost ~]$ echo "

    > http://

    > c.biancheng.net

    > "

    http://

    c.biancheng.net

    第一个 echo 命令在一行内输入完成,不会出现第二层提示符。第二个 echo 命令需要多行才能输入完成,提示符>用来告诉用户命令还没输入完成,请继续输入。

    echo 命令用来输出一个字符串。字符串是一组由" "包围起来的字符序列,echo 将第一个"作为字符串的开端,将第二个"作为字符串的结尾。对于第二个 echo 命令,我们将字符串分成多行,echo 遇到第一个"认为是不完整的字符串,所以会继续等待用户输入,直到遇见第二个"

    命令提示符的格式不是固定的,用户可以根据自己的喜好来修改,将在下一节展开讲解。

    九、Shell修改命令提示符

    Shell 通过PS1PS2这两个环境变量来控制提示符的格式,修改PS1PS2的值就能修改命令提示符的格式。

    • PS1 控制最外层的命令提示符格式。
    • PS2 控制第二层的命令提示符格式。

    在修改 PS1 和 PS2 之前,我们先用 echo 命令输出它们的值,看看默认情况下是什么样子的:

    [mozhiyan@localhost ~]$ echo $PS1

    [\u@\h \W]\$

    [mozhiyan@localhost ~]$ echo $PS2

    >

    Linux 使用以\为前导的特殊字符来表示命令提示符中包含的要素,这使得 PS1 和 PS2 的格式看起来可能有点奇怪。下表展示了可以在 PS1 和 PS2 中使用的特殊字符。

    Bash shell 命令提示符可以包含的要素

    字符描述
    \a铃声字符
    \d格式为“日 月 年”的日期
    \eASCII 转义字符
    \h本地主机名
    \H完全合格的限定域主机名
    \jshell 当前管理的作业数
    \1shell 终端设备名的基本名称
    \nASCII 换行字符
    \rASCII 回车
    \sshell 的名称
    \t格式为“小时:分钟:秒”的24小时制的当前时间
    \T格式为“小时:分钟:秒”的12小时制的当前时间
    \@格式为 am/pm 的12小时制的当前时间
    \u当前用户的用户名
    \vbash shell 的版本
    \Vbash shell 的发布级别
    \w当前工作目录
    \W当前工作目录的基本名称
    \!该命令的 bash shell 历史数
    \#该命令的命令数量
    \$如果是普通用户,则为美元符号$;如果超级用户(root 用户),则为井号#
    \nnn对应于八进制值 nnn 的字符
    \\斜杠
    \[控制码序列的开头
    \]控制码序列的结尾

    注意,所有的特殊字符均以反斜杠\开头,目的是与普通字符区分开来。您可以在命令提示符中使用以上任何特殊字符的组合。

    【实例】通过修改 PS1 变量的值来修改命令提示符的格式:

    [mozhiyan@localhost ~]$ PS1="[\t][\u]\$ "

    [12:51:43][mozhiyan]$ PS1="[c.biancheng.net]\$ "

    [c.biancheng.net]$

    第一次修改后可以显示当前的时间和用户名,第二次修改后显示C语言中文网的域名。为了保留版权,证明该教程出自C语言中文网,后续文章中我经常会使用[c.biancheng.net]$这种命令提示符,大家不要觉得奇怪。

    遗憾的是,通过这种方式修改的命令提示符只在当前的 Shell 会话期间有效,再次启动 Shell 后将重新使用默认的命令提示符。

    如果希望持久性地修改 PS1,让它对任何 Shell 会话都有效,那么就得把 PS1 变量的修改写入到 Shell 启动文件中,我们将在《十四、如何编写自己的Shell配置文件(配置脚本)?》一节中展开讨论。

    十、第一个Shell脚本

    几乎所有编程语言的教程都是从使用著名的“Hello World”开始的,出于对这种传统的尊重(或者说落入俗套),我们的第一个 Shell 脚本也输出“Hello World”。

    打开文本编辑器,新建一个文本文件,并命名为 test.sh。

    扩展名sh代表 shell,扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用php好了。

    在 test.sh 中输入代码:

    #!/bin/bash

    echo "Hello World !" #这是一条语句

    第 1 行的#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell;后面的/bin/bash就是指明了解释器的具体位置。

    第 2 行的 echo 命令用于向标准输出文件(Standard Output,stdout,一般就是指显示器)输出文本。在.sh文件中使用命令与在终端直接输入命令的效果是一样的。

    第 2 行的#及其后面的内容是注释。Shell 脚本中所有以#开头的都是注释(当然以#!开头的除外)。写脚本的时候,多写注释是非常有必要的,以方便其他人能看懂你的脚本,也方便后期自己维护时看懂自己的脚本——实际上,即便是自己写的脚本,在经过一段时间后也很容易忘记。

    下面给出了一段稍微复杂的 Shell 脚本:

    #!/bin/bash

    # Copyright (c) http://c.biancheng.net/shell/

    echo "What is your name?"

    read PERSON

    echo "Hello, $PERSON"

    第 5 行中表示从终端读取用户输入的数据,并赋值给 PERSON 变量。read 命令用来从标准输入文件(Standard Input,stdin,一般就是指键盘)读取用户输入的数据。

    第 6 行表示输出变量 PERSON 的内容。注意在变量名前边要加上$,否则变量名会作为字符串的一部分处理。

    十一、执行Shell脚本(多种方法)

    上节我们编写了一个简单的 Shell 脚本,这节我们就让它运行起来。运行 Shell 脚本有两种方法,一种在新进程中运行,一种是在当前 Shell 进程中运行。

    1、在新进程中运行 Shell 脚本

    在新进程中运行 Shell 脚本有多种方法。

    (1) 将 Shell 脚本作为程序运行

    Shell 脚本也是一种解释执行的程序,可以在终端直接调用(需要使用 chmod 命令给 Shell 脚本加上执行权限),如下所示:

    [mozhiyan@localhost ~]$ cd demo                         #切换到 test.sh 所在的目录

    [mozhiyan@localhost demo]$ chmod +x ./test.sh  #给脚本添加执行权限

    [mozhiyan@localhost demo]$ ./test.sh                  #执行脚本文件

    Hello World !                                                          #运行结果

    第 2 行中,chmod +x表示给 test.sh 增加执行权限。

    第 3 行中./表示当前目录,整条命令的意思是执行当前目录下的 test.sh 脚本。如果不写./,Linux 会到系统路径(由 PATH 环境变量指定)下查找 test.sh,而系统路径下显然不存在这个脚本,所以会执行失败。

    通过这种方式运行脚本,脚本文件第一行的#!/bin/bash一定要写对,好让系统查找到正确的解释器。

    (2) 将 Shell 脚本作为参数传递给 Bash 解释器

    你也可以直接运行 Bash 解释器,将脚本文件的名字作为参数传递给 Bash,如下所示:

    [mozhiyan@localhost ~]$ cd demo                                #切换到 test.sh 所在的目录

    [mozhiyan@localhost demo]$ /bin/bash test.sh            #使用Bash的绝对路径

    Hello World !                                                                  #运行结果

    通过这种方式运行脚本,不需要在脚本文件的第一行指定解释器信息,写了也没用。

    更加简洁的写法是运行 bash 命令。bash 是一个外部命令,Shell 会在 /bin 目录中找到对应的应用程序,也即 /bin/bash。

    [mozhiyan@localhost ~]$ cd demo

    [mozhiyan@localhost demo]$ bash test.sh

    Hello World !

    这两种写法在本质上是一样的:第一种写法给出了绝对路径,会直接运行 Bash 解释器;第二种写法通过 bash 命令找到 Bash 解释器所在的目录,然后再运行,只不过多了一个查找的过程而已。

    (3)检测是否开启了新进程

    有些读者可能会疑问,你怎么知道开启了新进程?你有什么证据吗?既然如此,那我就来给大家验证一下吧。

    Linux 中的每一个进程都有一个唯一的 ID,称为 PID,使用$$变量就可以获取当前进程的 PID,$$是 Shell 中的特殊变量。

    首先编写如下的脚本文件,并命名为 check.sh: 

    #!/bin/bash

    echo $$ #输出当前进程PID

    然后使用以上两种方式来运行 check.sh:

    [mozhiyan@localhost demo]$ echo $$

    2861 #当前进程的PID

    [mozhiyan@localhost demo]$ chmod +x ./check.sh

    [mozhiyan@localhost demo]$ ./check.sh

    4597 #新进程的PID

    [mozhiyan@localhost demo]$ echo

    $$ 2861 #当前进程的PID

    [mozhiyan@localhost demo]$ /bin/bash check.sh

    4584 #新进程的PID

    你看,进程的 PID 都不一样,当然就是两个进程了。

    2、在当前进程中运行 Shell 脚本

    这里需要引入一个新的命令——source 命令。source 是 Shell内置命令的一种,它会读取脚本文件中的代码,并依次执行所有语句。你也可以理解为,source 命令会强制执行脚本文件中的全部命令,而忽略脚本文件的权限。

    source 命令的用法为:

    source filename

    也可以简写为:

    . filename

    两种写法的效果相同。对于第二种写法,注意点号.和文件名中间有一个空格

    例如,使用 source 运行上节的 test.sh:

    [mozhiyan@localhost ~]$ cd demo                                  #切换到test.sh所在的目录

    [mozhiyan@localhost demo]$ source ./test.sh                #使用source

    Hello World !

    [mozhiyan@localhost demo]$ source test.sh                  #使用source

    Hello World !

    [mozhiyan@localhost demo]$ . ./test.sh                          #使用点号

    Hello World ! [mozhiyan@localhost demo]$ . test.sh       #使用点号

    Hello World !

    你看,使用 source 命令不用给脚本增加执行权限,并且写不写./都行,是不是很方便呢?

    检测是否在当前 Shell 进程中

    我们仍然借助$$变量来输出进程的 PID,如下所示:

    [mozhiyan@localhost ~]$ cd demo

    [mozhiyan@localhost demo]$ echo $$

    5169             #当前进程PID

    [mozhiyan@localhost demo]$ source ./check.sh

    5169            #Shell脚本所在进程PID

    [mozhiyan@localhost demo]$ echo $$

    5169            #当前进程PID

    [mozhiyan@localhost demo]$ . ./check.sh

    5169            #Shell脚本所在进程PID

    你看,进程的 PID 都是一样的,当然是同一个进程了

    3、总结

    作为初学者,你可能看不懂这些运行方式有什么区别,没关系,暂时先留个疑问吧,后续教程中我们会逐一讲解。

    如果需要在新进程中运行 Shell 脚本,我一般使用bash test.sh这种写法;如果在当前进程中运行 Shell 脚本,我一般使用. ./test.sh这种写法。这是我个人的风格。

    最后再给大家演示一个稍微复杂的例子。本例中使用 read 命令从键盘读取用户输入的内容并赋值给 URL 变量,最后在显示器上输出。

    #!/bin/bash

    # Copyright (c) http://c.biancheng.net/shell/

    echo "What is the url of the shell tutorial?"

    read URL

    echo "$URL is very fast!"

    运行脚本:

    [mozhiyan@localhost demo]$ . ./test.sh

    What is the url of the shell tutorial?

    http://c.biancheng.net/shell/↙

    http://c.biancheng.net/shell/ is very fast!

    表示按下回车键。

    十二、Shell四种运行方式(启动方式)精讲

    Shell 是一个应用程序,它的一端连接着 Linux 内核,另一端连接着用户。Shell 是用户和 Linux 系统沟通的桥梁,我们都是通过 Shell 来管理 Linux 系统。

    我们可以直接使用 Shell,也可以输入用户名和密码后再使用 Shell;第一种叫做非登录式,第二种叫做登录式。

    我们可以在 Shell 中一个个地输入命令并及时查看它们的输出结果,整个过程都在跟 Shell 不停地互动,这叫做交互式。我们也可以运行一个 Shell 脚本文件,让所有命令批量化、一次性地执行,这叫做非交互式。

    总起来说,Shell 一共有四种运行方式:

    • 交互式的登录 Shell;
    • 交互式的非登录 Shell;
    • 非交互式的登录 Shell;
    • 非交互式的非登录 Shell。

    1、判断 Shell 是否是交互式

    判断是否为交互式 Shell 有两种简单的方法。

    1) 查看变量-的值,如果值中包含了字母i,则表示交互式(interactive)。

    【实例1】在 CentOS GNOME 桌面环境自带的终端下输出-的值:

    [c.biancheng.net]$ echo $- himBH

    包含了i,为交互式。

    【实例2】在 Shell 脚本文件中输出-的值:

    [c.biancheng.net]$ cat test.sh

    #!/bin/bash

    echo $-

    [c.biancheng.net]$ bash ./test.sh

    hB

    不包含i,为非交互式。注意,必须在新进程中运行Shell脚本(见第十一节:运行Shell脚本)。

    2) 查看变量PS1的值,如果非空,则为交互式,否则为非交互式,因为非交互式会清空该变量。

    【实例1】在 CentOS GNOME 桌面环境自带的终端下输出 PS1 的值:

    [mozhiyan@localhost]$ echo $PS1

    [\u@\h \W]\$

    非空,为交互式。

    【实例2】在 Shell 脚本文件中输出 PS1 的值:

    [c.biancheng.net]$ cat test.sh

    #!/bin/bash

    echo $PS1

    [c.biancheng.net]$ bash ./test.sh

    空值,为非交互式。注意,必须在新进程中运行 Shell 脚本。

    2、判断 Shell 是否为登录式

    判断 Shell 是否为登录式也非常简单,只需执行shopt login_shell即可,值为on表示为登录式,off为非登录式。

    shopt 命令用来查看或设置 Shell 中的行为选项,这些选项可以增强 Shell 的易用性。

    【实例1】在 CentOS GNOME 桌面环境自带的终端下查看 login_shell 选项:

    [c.biancheng.net]$ shopt login_shell 

    login_shell      off

    【实例2】按下Ctrl+Alt+Fn组合键切换到虚拟终端,输入用户名和密码登录后,再查看 login_shell 选项:

    [c.biancheng.net]$ shopt login_shell

    login_shell          on

    【实例3】在 Shell 脚本文件中查看 login_shel 选项:

    [c.biancheng.net]$ cat test.sh

    #!/bin/bash

    shopt login_shell

    [c.biancheng.net]$ bash ./test.sh

    login_shell          off

    3、同时判断交互式、登录式

    要同时判断是否为交互式和登录式,可以简单使用如下的命令:

    echo $PS1; shopt login_shell

    或者

    echo $-; shopt login_shell

    4、常见的 Shell 启动方式

    1) 通过 Linux 控制台(不是桌面环境自带的终端)或者 ssh 登录 Shell 时(这才是正常登录方式),为交互式的登录 Shell。

    [c.biancheng.net]$ echo $PS1;shopt login_shell

    [\u@\h \W]\$

    login_shell          on

    2) 执行 bash 命令时默认是非登录的,增加--login选项(简写为-l后变成登录式。

    [c.biancheng.net]$ cat test.sh

    #!/bin/bash

    echo $-; shopt login_shell

    [c.biancheng.net]$ bash -l ./test.sh

    hB

    login_shell          on

    3) 使用由()包围的组命令或者命令替换进入子 Shell 时,子 Shell 会继承父 Shell 的交互和登录属性。

    [c.biancheng.net]$ bash

    [c.biancheng.net]$ (echo $PS1;shopt login_shell)

    [\u@\h \W]\$

    login_shell          off

    [c.biancheng.net]$ bash -l

    [c.biancheng.net]$

    (echo $PS1;shopt login_shell)

    [\u@\h \W]\$

    login_shell         on

    4) ssh 执行远程命令,但不登录时,为非交互非登录式。

    [c.biancheng.net]$ ssh localhost 'echo $PS1;shopt login_shell'

    login_shell          off

    5) 在 Linux桌面环境打开终端时,为交互式的非登录 Shell

    桌面环境下打开终端为交互式的非登录Shell

    ​​十三、Shell配置文件(配置脚本)的加载

    无论是否是交互式,是否是登录式,Bash Shell 在启动时总要配置其运行环境,例如初始化环境变量、设置命令提示符、指定系统命令路径等。这个过程是通过加载一系列配置文件完成的,这些配置文件其实就是 Shell 脚本文件。

    与 Bash Shell 有关的配置文件主要有 /etc/profile、~/.bash_profile、~/.bash_login、~/.profile、~/.bashrc、/etc/bashrc、/etc/profile.d/*.sh,不同的启动方式会加载不同的配置文件。

    ~表示用户主目录。 *是通配符,/etc/profile.d/*.sh 表示 /etc/profile.d/ 目录下所有的脚本文件(以 .sh结尾的文件)。

    1、登录式的 Shell

    Bash 官方文档说:如果是登录式的 Shell,首先会读取和执行 /etc/profiles,这是所有用户的全局配置文件,接着会到用户主目录中寻找 ~/.bash_profile、~/.bash_login 或者 ~/.profile,它们都是用户个人的配置文件。

    不同的 Linux 发行版附带的个人配置文件也不同,有的可能只有其中一个,有的可能三者都有,笔者使用的是 CentOS 7,该发行版只有 ~/.bash_profile,其它两个都没有。

    如果三个文件同时存在的话,到底应该加载哪一个呢?它们的优先级顺序是 ~/.bash_profile > ~/.bash_login > ~/.profile。

    如果 ~/.bash_profile 存在,那么一切以该文件为准,并且到此结束,不再加载其它的配置文件。

    如果 ~/.bash_profile 不存在,那么尝试加载 ~/.bash_login。~/.bash_login 存在的话就到此结束,不存在的话就加载 ~/.profile。

    注意,/etc/profiles 文件还会嵌套加载 /etc/profile.d/*.sh,请看下面的代码:

    for i in /etc/profile.d/*.sh ; do

             if [ -r "$i" ]; then

                     if [ "${-#*i}" != "$-" ]; then

                             . "$i"

                     else

                             . "$i" >/dev/null

                     fi

             fi

    done

    同样,~/.bash_profile 也使用类似的方式加载 ~/.bashrc

    if [ -f ~/.bashrc ]; then

            . ~/.bashrc

    fi

    2、非登录的 Shell

    如果以非登录的方式启动 Shell,那么就不会读取以上所说的配置文件,而是直接读取 ~/.bashrc。

    ~/.bashrc 文件还会嵌套加载 /etc/bashrc,请看下面的代码:

    if [ -f /etc/bashrc ]; then

            . /etc/bashrc

    fi

    十四 、如何编写自己的Shell配置文件(配置脚本)?

    学习了《十三、Shell配置文件(配置脚本)的加载》一节,读者应该知道 Shell 在登录和非登录时都会加载哪些配置文件了。对于普通用户来说,也许 ~/.bashrc 才是最重要的文件,因为不管是否登录都会加载该文件。

    我们可以将自己的一些代码添加到 ~/.bashrc,这样每次启动 Shell 都可以个性化地配置。如果你有代码洁癖,也可以将自己编写的代码放到一个新文件中(假设叫 myconf.sh),只要在 ~/.bashrc 中使用类似. ./myconf.sh的形式将新文件引入进来就行了

    使用 source 命令引入其它代码文件时有一些细节需要注意,我们将在后面展开讨论。

    实例1:给 PATH 变量增加新的路径

    你曾经是否感到迷惑,Shell 是怎样知道去哪里找到我们输入的命令的?例如,当我们输入 ls 后,Shell 不会查找整个计算机系统,而是在指定的几个目录中检索(最终在 /bin/ 目录中找到了 ls 程序),这些目录就包含在 PATH 变量中。

    当用户登录 Shell 时,PATH 变量会在 /etc/profile 文件中设置,然后在 ~/.bash_profile 也会增加几个目录。如果没有登录 Shell,PATH 变量会在 /etc/bashrc 文件中设置。

    如果我们想增加自己的路径,可以将该路径放在 ~/.bashrc 文件中,例如:

    PATH=$PATH:$HOME/addon

    将主目录下的 addon 目录也设置为系统路径。假如此时在 addon 目录下有一个 getsum 程序,它的作用是计算从 m 累加到 n 的和,那么我们不用 cd 到 addon 目录,直接输入 getsum 命令就能得到结果。

    《一、六:Shell命令的本质到底是什么?》一节中我已经给出了 getsum 程序及其源代码,有兴趣的读者可以猛击这里下载。下载完成后请配置环境变量,然后输入如下的命令就可以得到结果:

    [c.biancheng.net]$ getsum -s 1 -e 100

    5050

    -s选项表示起始(start)数字,-e选项表示终止(end)数字,以上命令用来计算从 1 累加到 100 的和。

    实例2:修改命令提示符的格式

    《九、修改Linux命令提示符》一节中我曾提到,修改 PS1 变量的值就可以修改命令提示符的格式,但是那个时候大家还不了解 Shell 启动文件,所以只能临时性地修改,并不能持久。

    现在我们已经知道,在 ~/.bashrc 文件中修改 PS1 变量的值就可以持久化,每个使用 Shell 的用户都会看见新的命令提示符。

    将下面的代码添加到 ~/.bashrc 文件中,然后重新启动 Shell,命令提示符就变成了[c.biancheng.net]$

    PS1="[c.biancheng.net]\$ "

  • 相关阅读:
    【RuoYi-Vue-Plus】学习笔记 44 - XSS 过滤器以及 @Xss 注解简单分析
    2022最新“Java面试宝典”,8大模块+18个专题,看完你就能进大厂
    el-upload上传图片到七牛云或阿里云
    章十九、JavaVUE —— 框架、指令、声明周期、Vue-cli、组件路由、Element
    STM32—按键控制LED(定时器)
    asio中的定时器steady_timer和deadline_timer
    CVE-2022-39227jwt伪造
    csapp-Machine-Level Representation of Program-review
    yolov5s V6.1版本训练PASCAL VOC2012数据集&yolov5官网教程Train Custom Data
    Android入门第31天-Android里的ViewFlipper翻转视图的使用
  • 原文地址:https://blog.csdn.net/sinat_41942180/article/details/134225709