• Linux之(9)shell基础概念(1)


    Linux之shell基础概念(1)

    Author:onceday Date:2022年9月12日

    漫漫长路,有人对你微笑过嘛…

    本文主要收集整理于以下文档:

    • 《Linux命令行与shell脚本编程大全》
    • 《鸟哥的Linux私房菜》
    1.引言

    整个Linux学习的终极目标就是编写自动化脚本,无论是文件、磁盘、进程、账户等,到了最后,不可能每一次都重新输入所有的命令。

    这和编程的也不一样,编程的开发,不会去操心这么多的命令怎么用,编程也无法像脚本一样随写随用。从事实来说,编程和linux命令行是两种技能,熟练掌握linux命令行及其有利于学习和工作。

    我们必须了解常见shell,即bash shell的语法知识,这将Linux学习的一个阶段性的里程碑。

    2.Bash shell基础概念
    2.1 历史命令、Tab补全和命令别名

    用过的bash shell的人都会发现, shell会记录已使用过的命令情况, 但按下上、下方向键,便可以浏览历史命令,这功能十分好用,可以减少重新性的命令输入。

    需要注意,当shell在运行的时候,历史命令缓存在内存中,在结束shell后,才会写入~/.bash_history中。因此,当bash shell非正常退出后,会发现丢失了此次的历史命令。

    命令描述
    history查看最近使用过的命令列表
    history -a强制将命令历史记录写入/etc/.bash_history
    history -n强制重新读取/etc/.bash_history中历史命令。

    bash shell另一个好用的功能无疑是Tab补全,可以减少输入的复杂度,特别是文件名,减少了工作量。

    在不同情况下,具有两种补全功能:

    • Tab接在一串命令的第一个字的后面,则为命令补全。
    • Tab接在一串命令的第二个字的后面,则为文件补齐。

    还有一个功能是命令别名Alias,由于每个命令的参数很多,输出形式也不一样,所以每个人都有自己喜欢的命令格式。Alias极大解决了这个问题,通过设置命令别名,可以搭建自己的命令集合。

    alias ll='ls -al'
    
    • 1

    取消命令别名:

    unalias ll
    
    • 1
    2.2 通配符 Wildcard

    通配符可用来过滤文件或者输出的结果,拥有和正则表达式类似的结果。

    2.3 type查询是否为Bash shell的内置命令

    许多命令都已被内置在bash中,因此有时候,我们想知道一个命令是否是内置命令、别名命令,还是外部命令,此时就需要使用type了。

    type cmd_name  #显示name命令是内置命令还是外部命令
    type -t name   #直接以file、alias、builtin三个关键字显示命令的类别
    type -P name   #如果后面的命令为外部命令才会显示完整的外部名
    type -a name   #显示PATH路径中所有的名为name的命令,包含alias
    
    • 1
    • 2
    • 3
    • 4
    2.4 换行和快速移位

    如果一行命令的参数太长,写不下,可以通过\转义Enter键,即:

    onceday@ubuntu:~$ cp hello.c \
    > hello2.c
    
    • 1
    • 2

    再输入回车即可执行。一般命令行可以显示的列数都在80~100之间, 这受到诸多方面的限制。比如,我就不喜欢开一个特别大的命令行窗口,全屏输入命令这种行为我是一点都不喜欢的。

    第二个就是快速操作已输入的字符串:

    组合键描述
    Ctrl+u从光标处向前删除整个字符串,用于快速删除前缀内容
    Ctrl+k从光标处向后删除整个字符串,用于快速删除后缀内容
    Ctrl+a让光标移动到整个命令串的最前面
    Ctrl+e让光标移动到整个命令串的最后面
    3.shell并行和父子关系

    当登入终端时,第一个启动shell就是父shell,后续启动的shell都算是其的子shell。要想观察这情况,需要使用ps -f命令,显示当前进程及子进程的具体情况。

    onceday@ubuntu:~$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    onceday     12    11  0 14:08 pts/0    00:00:00 -bash
    onceday     85    12  0 14:45 pts/0    00:00:00 ps -f
    
    • 1
    • 2
    • 3
    • 4

    可以看到在输入ps -f后,当前的shell进程会启动一个子进程,PPID是父进程ID,很明显,ps -f进程的父ID就是当前的bash shell进程。

    这种父子shell进程的关系是可以嵌套的,如下:

    onceday@ubuntu:~$ bash
    onceday@ubuntu:~$ bash
    onceday@ubuntu:~$ bash
    onceday@ubuntu:~$ ps --forest
      PID TTY          TIME CMD
       12 pts/0    00:00:00 bash
       86 pts/0    00:00:00  \_ bash
       92 pts/0    00:00:00      \_ bash
       98 pts/0    00:00:00          \_ bash
      104 pts/0    00:00:00              \_ ps
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用exit即可退出子进程,bash支持以下命令行参数:

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

    通过使用;可以一次指定多条需要一次运行的命令。如下:

    pwd; ls; cd /etc; pwd; cd; pwd; ls
    
    • 1

    这种形式下,每个命令依次执行,等待其他的命令执行完毕,然后执行自己的命令。

    如果再加上括号(),则会将括号里命令放在一个子shell里面执行,即这些命令依次在子shell里面执行完毕。

    3.2 后台模式

    上面直接使用括号包含命令,确实能够生成子shell执行,但此时父shell也被霸占,无法处理额外的命令。

    直接使用命令后缀&符号,即可将一个命令放入后台运行。此时父shell还可以进行交互。

    onceday@ubuntu:~$ sleep 20 &
    [1] 125
    onceday@ubuntu:~$ ps -f
    UID        PID  PPID  C STIME TTY          TIME CMD
    onceday     12    11  0 14:08 pts/0    00:00:00 -bash
    onceday    125    12  0 15:17 pts/0    00:00:00 sleep 20
    onceday    126    12  0 15:17 pts/0    00:00:00 ps -f
    onceday@ubuntu:~$ jobs
    [1]+  Running                 sleep 20 &
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以看到,返回的125即是其对应的PID,shell会创建一个子shell来执行后台命令。

    也可以使用jobs来查看显示后台作业信息,可以显示当前运行在后台模式中所有用户的进程。

    使用进程列表+后台模式,可以将子shell 放在后台运行

    (sleep 10; echo $BASH_SUBSHELL; sleep 10)&
    
    • 1
    3.3 协程coproc

    使用coproc后接命令或者命令列表,即可创造协程,会在后台生成一个子shell,并在这个子shell中执行这个命令或者命令列表。

    onceday@ubuntu:~$ coproc sleep 10;
    [2]159
    onceday@ubuntu:~$ jobs
    [2]+  Running                 coproc COPROC sleep 10 &
    
    • 1
    • 2
    • 3
    • 4

    默认协程会有一个名字COPROC,这个名字可以自定义,如下:

    onceday@ubuntu:~$ coproc once { sleep 10; }                                            [1] 153
    
    • 1

    当有多个协程运行,并且之间需要通信的时候,协程的名字才是有价值的。

    需要注意,括号和命令之间,以及结尾的分号都是必须的,否则会报语法错误

    4.Shell环境变量

    环境变量可以用来存储有关shell会话和工作环境的信息。

    一般有两种:

    • 全局变量,对所有的shell都是可见的。
    • 局部变量,只对创建他们的shell可见。

    以下是几种输出变量的方式:

    命令描述
    env输出所有全局变量
    printenv输出全部全局变量
    printenv HOME输出某个全局变量的值
    echo $HOME显示某个变量的值
    set输出全部变量(局部变量、全局变量、用户自定义变量)的值

    在bash上,当一个变量没有值(尚未设置)时,其用echo显示会为空。但不同的shell上情况时不一样的。

    4.1 设置局部变量

    直接使用var_name=var_value就可以创建局部变量。需要注意以下事项:

    • 等号两边不能有空格
    • 变量名称只能是英文字母与数字,且开头不能为数字
    • 变量内容有空格可用'x x'"x x"来书写。需要注意单引号是原始字符串,不会对里面的特殊字符进行操作,如$name,单引号不会替换值,而双引号会替换值。
    • 变量的值中可以使用\转义字符,将Enter$\空格 等变成一般字符
    • 可以使用大括号括起变量名,如${var_name}xxx

    变量的值是可以拼接的:

    PATH=“$PATH":/home/bin
    PATH=${PATH}:/home/bin
    
    • 1
    • 2

    需要注意,系统环境变量的名字都是大写的,因此,为了避免不必要的冲突,用户自定义的变量最好是小写

    4.2 设置、删除全局变量

    局部变量只在当前的shell中生效,比如在子shell中,就无法读取父shell中定义的局部变量的值。

    设置全局变量很简单,使用export即可。

    my_var="I am a variable"
    export my_var
    
    • 1
    • 2

    注意:虽然子shell可以继承父shell的全局变量,但是是两个不同的进程,因此子shell对全局变量的改变时无法传递到父shell,因为它们属于不同进程的全局变量

    删除全局变量使用unset就行:

    unset my_var
    
    • 1

    注意这只对当前的shell进程生效

    4.3 变量内容的删除、取代和替换

    设置和删除变量:

    设置方式说明
    ${变量#关键词}若变量从头开始的数据符合(关键词),则将符合的最短数据删除
    ${变量##关键词}若变量内容从头开始的数据符合(关键词),则将最符合的最长数据删除
    ${变量%关键词}若变量内容从尾向前的数据符合(关键词),则将最符合的最短数据删除
    ${变量%%关键词}若变量内容从尾部向前的数据符合(关键词),则将符合的最长数据删除
    ${变量/旧字符串/新字符串}若变量内容符合(旧字符串)则(第一个旧字符串会被新字符串替换)
    ${变量//旧字符串/新字符串}若变量内容符合(旧字符串)则(全部的旧字符串会被新字符串替换)

    例如:

    FILEPATH=/var/spool/mail/onceday
    ${FILEPATH##/*/}
    #输出为onceday,即只保留文件名。
    ${FILEPATH%/*}
    #输出为/var/spool/mail/,即保留路径,删除文件名
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试变量内容并设置(空字符串即var=“”):

    变量设置方式str没有设置str为空字符串str已设置为非空字符串
    var=${str-expr}var=exprvar=“”var=$str
    var=${str:-expr}var=exprvar=exprvar=$str
    var=${str+expr}var=“”var=exprvar=expr
    var=${str:+expr}var=“”var=“”var=expr
    var=${str=expr}str=expr, var=exprstr=“”, var=“”str不变, var=$str
    var=${str:=expr}str=expr, var=exprstr=expr, var=exprstr不变, var=$str
    var=${str?expr}expr 输出至 stderrvar=“”var=$str
    var=${str:?expr}expr 输出至 stderrexpr 输出至stderrvar=$str
    4.4 默认全局变量

    这些默认变量主要源自于Unix Bourne shell,下面只摘录了部分,详细可以通过man bash查看。

    变量描述
    CDPATH冒号分割的目录列表,用于cd命令的搜索路径
    HOME当前用户的主目录
    PATHshell查找命令的目录列表,由冒号分割
    PS1shell命令行界面的主提示符
    PS2shell命令行界面的次提示符
    PS3select命令的提示符
    PS4如果使用了bash的-x选项,在命令行之前显示的提示信息
    BASH当前shell的全路径名
    BASH_ENVbash启动前尝试运行的脚本文件
    BASHPID当前bash进程的PID
    COLUMNS当前bash shell实例所用终端的宽度
    FUNCNAME当前执行的shell函数的名称
    FUNCNEST设置非零值时,所允许的最大函数嵌套级数
    HISTFILE保存shell历史记录列表的文件名
    HISTFILESIZE最多在历史文件中存在多少行
    HISTSIZE最多在历史文件中存在多少条命令
    HOSTNAME当前主机的名称
    HOSTTYPE当前运行bash shell的机器
    LANGshell的语言环境类别
    LC_ALL定义一个语言环境类别,能够覆盖LANG变量
    LINES定义了终端上可见的行数
    MACHTYPE用CPU-公司-系统格式定义的系统类型
    OLDPWDshell之前的工作目录
    OSTYPE定义了shell所在的操作系统
    PPIDshell父进程的ID
    PWD当前工作目录
    RANDOM返回一个0~32767的随机数,可赋值成为随机数种子
    SECONDS自从shell启动到现在的秒数(对其赋值将会重置计数器)
    SHELLbash shell的全路径名
    SHLVLshell的层级,每次启动一个新bash shell,该值增加1
    TIMEFORMAT指定了shell的时间显示格式
    TMOUTselect和read命令在没输入的情况下等待多久,默认值为0,表示无限长。
    TMPDIR目录名,保存bash shell创建的临时文件
    UID当前用户的真实用户ID
    4.5 PS1提示符的设置

    这个就是命令行在输入命令之前出现的提示符内容:

    onceday@ubuntu:~$ echo $PS1
    \[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$
    
    • 1
    • 2

    其中掺杂的数字字符,是用来控制显示颜色的,而\e等这些, 是一些特殊的符号,下面是它们的含义:

    符号含义
    \d显示出日期的格式,[Mon Feb 2]
    \H完整的主机名
    \h仅取主机名在第一个小数点之前的名字
    \t显示时间,为24小时格式: HH:MM:SS
    \T显示时间,为24小时格式: HH:MM:SS
    \A显示时间,为24小时格式: HH:MM
    @显示时间,为12小时格式: am/pm
    \u目前用户的账号名称
    \vBASH的版本信息
    \w完整的工作目录
    \W利用basename函数取得工作目录的名称,仅会列出最后一个目录
    \#执行的第几个命令
    \$提示字符,如果是root,提示字符为#,否则就是$

    色彩控制符的格式如下:\[\033[01;32m\]\u@\h\[\033[00m\]

    显示方式:\033[显示方式;前景色;背景色 + m + 正文 + \033[0m.

    显示方式意义
    0终端默认设置
    1高亮显示
    4使用下划线
    5闪烁
    7反白显示
    8不可见

    下面是色彩的控制代码:

    前景色背景色颜色
    3040黑色
    3141红色
    3242绿色
    3343黃色
    3444蓝色
    3545紫红色
    3646青蓝色
    3747白色
    5.不同的shell启动方式

    shell有三种启动方式:

    • 登入时作为默认登入shell
    • 作为非登入shell的交互式shell
    • 作为运行脚本的非交互式shell
    5.1 登入shell

    默认登入shell会从5个不同的启动文件里读取命令:

    • /etc/profile
    • $HOME/.bash_profile
    • $HOME/.bashrc
    • $HOME/.bash_login
    • $HOME/.profile

    如果使用可拆卸认证模块,Pluggable Authentication Modules, PAM。那么还可能读入以下两个文件:

    • /etc/environment
    • $HOME/.pam_environment

    显然,/etc/profile是所有用户共享的启动文件。在这个文件里,一般会遍历一个启动脚本的文件夹:

    • /etc/profile.d/

    可以把需要启动就运行的脚本放在这些文件夹里面。

    需要注意的是,后面三个HOME里面的启动文件,并不会同时都加载,具体需要看/etc/profile里面的代码

    5.2 交互式shell

    非登入时启动的交互式shell,如在bash里面启动的shell。

    这样的交互式shell不会访问/etc/profile文件,但是会检查$HOME/.bashrc文件

    5.3 非交互式shell

    系统运行脚本,或者编程运行脚本就是这样。

    这种shell可以通过BASH_ENV环境变量来检查是否有要执行的启动文件。

    5.4 环境变量持久化

    自定义的变量可以存放于/etc/profile里面,但是如果系统更新或者版本更新,那么这个文件很有可能被重置。因此,最好把文件放在/etc/profile.d上,这样不容易被覆盖。

    5.5 bash登入和欢迎信息

    /etc/issue可以填写tty1~tty6的登入欢迎信息。

    onceday@ubuntu:~$ cat /etc/issue
    Ubuntu 20.04.3 LTS \n \l 
    
    • 1
    • 2

    \n\l是变量,具体信息可以在man issue里面查看。

    变量符号描述
    \d本地端时间的日期
    \l显示第几个终端界面
    \m显示硬件的等级
    \n显示主机的网络名称
    \O显示domain name
    \r操作系统的版本,相当于uname -r
    \t显示本地端时间的时间
    \S操作系统的名称
    \v操作系统的版本
    5.6 终端的环境设置

    使用stty -a可以列出目前环境的所有按键列表。

    默认的按键列表如下:

    组合按键执行结果
    Ctrl+C终止目前的命令
    Ctrl+D输入结束(EOF),例如邮件结束的时候
    Ctrl+M回车
    Ctrl+S暂停屏幕的输出
    Ctrl+Q恢复屏幕的输出
    Ctrl+U在提示字符下,将整列命令删除
    Ctrl+Z暂停目前的命令
    6.通配符与特殊符号
    6.1 通配符Wildcard
    符号意义
    *代表0到无穷多个任意字符
    ?代表至少有一个任意字符
    [ ]同样代表一定有一个在中括号内的字符,如[abc]代表a,b,c中任意一个
    [ - ]在顺序编码内的所有字符,如[a-z]代表所有小写字母
    [^]^代表反向选择,[^a-b]代表非小写字母的任意一个字符

    示例:

    #找出以corn开头的文件名
    ll -d /etc/cron* 
    #找出刚好是五个字符的文件名
    ll -d /etc/?????
    #找出含有数字的文件名
    ll -d /etc/*[0-9]*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    6.2 特殊符号
    符号内容
    #注释符号
    \转义符号,将特殊符号和通配符还原成一般字符
    |管道(pipe):分割两个管道命令的符号
    ;连续命令执行分隔符:连续性命令的界定
    ~用户的家目录
    $使用变量前导符,亦即是变量之前需要加的变量替换值
    &任务管理(job control):将命令变成后台任务
    !逻辑运算意义上的非not的意思
    /目录符号:路径分割的符号
    >,>>数据流重定向,>是替换,>>是叠加
    <,<<数据流重定向,输入定向
    ’ ’单引号,不具有变量替换的功能
    “ ”具有变量替换的功能
    ``两个分词符中间为可以先执行的命令,也可以使用$( )
    ( )在中间为子shell的起始与结束
    { }在中间为命令区块的组合
  • 相关阅读:
    为什么要认真准备Java面试,编程语言排行榜告诉你
    【使用 BERT 的问答系统】第 5 章 :BERT模型应用:问答系统
    【C++】日期类实现,与日期计算相关OJ题
    提升爬虫IP时效:解决被封IP的难题
    R语言ggplot2可视化:使用ggpubr包的ggbarplot函数可视化柱状图、palette参数自定义不同水平柱状图边框以及填充的颜色
    Java高级---Spring Boot---7数据访问
    Ansible中的变量及加密
    高质量 Spring 实战学习笔记,腾讯内部学习 Spring 首推
    Ingress安全网关
    快来直播带你了解中国互联网大厂布局元宇宙现状如何?
  • 原文地址:https://blog.csdn.net/Once_day/article/details/126843179