Linux 命令行与 shell 脚本编程大全 11 构建基础脚本

介绍编写 shell 脚本的基础知识

更多精彩

导览

  1. 可以使用分号在同一行中执行多个命令
  2. 在创建 shell 脚本时,必须指定 shell 脚本将要使用的 shell 类型,例如 #! /bin/bash ,表示使用 bash shell 作为 shell 脚本的执行环境
  3. 变量的声明不需要添加任何前缀,但变量的使用需要添加 美元符号( $ ) 作为前缀
  4. 变量的声明及赋值时,变量名、等号、变量值之间不能有空格
  5. 可以使用 $() 来实现 命令替换 的效果
  6. 使用 大于号( > ) 可以实现 输出重定向 ,也就是将命令结果输出到文件
  7. 使用 双大于号( >> ) 可以在实现 输出重定向 的同时,向已有的文件追加输出结果,而不是替换
  8. 使用 小于号( < ) 可以实现 输入重定向 ,也就是将文件内容输入到命令
  9. 使用 双小于号( << ) 以及自定义的标识,可以实现 内联输入重定向 ,也就是不需要实体文件既可以将内容输入到命令
  10. 可是使用 管道连接 将一个命令的结果输出到另一个命令
  11. 使用 expr 命令可以实现数学运算,但不是很好用,有些操作符会涉及到 shell 的关键字,需要使用反斜线转义后才能使用
  12. 使用 $[] 符号也可以实现数学运算,而且操作符不会涉及到 shell 关键字
  13. 使用 expr 命令或 $[] 符号都无法实现浮点运算,但是使用 Linux 内建的 bc 计算器可是实现浮点运算
  14. 输入 bc 可以进入计算器模式,其中的 scale 参数可以指定两数相除后的结果精确到小数点后几位
  15. shell 中的每个命令执行结束后都会返回一个 0-255 之间的 退出状态码 ,使用 echo $? 命令可以输出上一条命令的 退出状态码
  16. exit 命令后追加数字,可以为 shell 脚本指定一个自定义的 退出状态码

11.1 使用多个命令

  1. 如果要让两个命令一起运行,可以将其放在同一行,并使用分号分割,如下图
    • 这是在第 5 章学过的 命令列表
    • 只要 同一行执行的命令不超过 255 个字符 就行

11.2 创建 shell 脚本文件

  1. 创建 shell 脚本文件,其实就是创建一个普通文件,在文件中放置需要执行的 shell 脚本
  2. 为了表明这个文件是用来执行 shell 脚本的,需要在文件第一行执行其中脚本需要使用的 shell 类型,比如 #! /bin/bash 就表示文件内的脚本需要使用 bash shell 类执行,如下图
    • 在 shell 中,# 号通常被识别为注释的标识,但第一行的 #! 例外
    • shell 会根据命令出现的顺序依次执行,比如在以下脚本中,date 命令会先执行,然后再执行 who 命令

11.2.1 执行 shell 脚本

  1. 创建 shell 脚本之后,下一步就是执行文件,如下图
    • 之前创建了 testShell 的 shell 脚本文件,按照我们通常的命令执行经验,执行输入命令名称之后按回车即可
  2. 但当输入 testShell 命令后,得到的结果是 未找到命令 ,这个时候我们应该想起来第 6 章中学到的关于 PATH 环境变量 的知识
    • PATH 环境变量 的知识我们可以知道,Linux 在获取命令时默认只会扫描被 PATH 环境变量 覆盖的路径
    • 很显然我们执行命令的当前命令不在 PATH 环境变量 中,那么解决方式有哪些?是将当前路径添加到 PATH 环境变量 中吗?
    • 很显然将当前这个临时测试使用的命令添加到 PATH 环境变量 中不是一个好的选择
    • 应该是 使用绝对路径来执行命令 ,所以应该输入 ./testShell
  3. 但输入 ./testShell 命令后,得到的结果是 权限不足 ,这个时候我们应该想起来第 7 章学到的关于 umask 设置默认文件权限 的知识
    • umask 命令设置的默认文件权限是 022 ,也就是下图中 testShell 显示的 rw-r--r--
    • 所以就算是 root 用户,对于该文件默认也只有读写权限,没有执行权限


  4. 使用 chmod u+x testShell 命令为该文件赋予执行权限后就可以使用 ./testShell 执行该脚本,效果如下图

11.3 使用 echo 命令显示消息

  1. echo 命令后面添加一个字符串后再执行命令,这个字符串就会被输出在终端,如下图
  2. 如果输出的字符串中存在引号,则可以使用单引号和双引号配合的方式输出结果,学过 JS 的对这种操作不会陌生,如下图
    • 如果直接输出引号,终端会命令没有结束,所以需要根据实际情况决定是使用单引号还是双引号
    • 需要注意的是,如果需要在引号中引用变量,则需要使用双引号包裹,否则变量不会被识别
  3. 使用 echo -n 命令可以让输出的字符串和下一个命令的输出结果在同一行,如下图

11.4 使用变量

  1. 在 shell 脚本中也可以使用变量
  2. 变量的作用是将通过 shell 脚本获取的信息临时的存储在脚本中,当后续脚本需要用到这些信息时,可以通过这些变量进去获取

11.4.1 环境变量

  1. 在第 6 章学习过系统环境变量,这些变量在 shell 脚本中也是可以直接引用的,使用 美元符号 ( $ ) 即可,如下图
    • 左侧是 shell 脚本的内容,右侧是执行结果


  2. 那么如果待输出的字符串中希望直接输出美元符号,而不是作为环境变量的获取符,应该如何操作?如下图
    • 可以看到,只需要在美元符号前面加一个 反斜线 ( \ ) 即可

11.4.2 用户变量

  1. 真正在 shell 脚本中发挥作用的变量肯定还是用户自定义变量
  2. 用户变量可以是由任何字母、数字或下划线组成的文本字符串
    • 单个变量名称的长度 不超过 20 个字符 即可
    • 区分大小写,varVar 不是同一个变量
  3. 变量的声明和赋值方式是 variableName=variableValue
    • 变量名、等号、变量值之间不能有等号,否则会报错
  4. 变量在声明时不需要指定类型,直接声明即可,shell 脚本会自动决定变量值的数据类型
    • 这一点说起来就像是 JS 的弱类型特点一样,但其实比 JS 还要简单
    • 因为 JS 的变量声明是 var variableName=variableValue
    • 而 shell 的变量声明是 variableName=variableValue ,连前置的弱类型标识都省略了
  5. 声明变量时不需要任何前缀,但引用变量时则需要使用 美元符号( $ ) ,如下图
    • 可以看到,如果不使用美元符号作为前缀,变量根本就不会被获取


11.4.3 命令替换

  1. 如果需要将命令的输出赋予变量,需要使用的特性就是 命令替换
  2. 命令替换 在执行时,实际上不是直接执行,而是开启一个子 shell 来执行需要替换的命令,之后再将结果赋予变量
  3. 实现 命令替换 的方式两种,如下图

11.5 重定向输入和输出

11.5.1 输出重定向

  1. 将命令的输出通过 大于号( > ) 发送到指定文件中,就是 输出重定向 ,如下图
  2. 如果重定向的目标文件已存在,则会直接用新内容覆盖,如下图


  3. 那么如果不想旧内容被覆盖,则可使用 双大于号( >> ) 进行数据追加,如下图

11.5.2 输入重定向

  1. 输出重定向 相反,将文件中的内容通过 小于号( < ) 输出到命令,就是 输入重定向 ,如下图
    • testShell 文件中的内容输出到 sort 命令后,命令执行的结果是对文件中的内容进行了排序后再输出

11.5.2.1 内联输入重定向

  1. 所谓 内联 ,就是不使用文件,直接在终端将内容输入到命令,如下图
    • 可以看到,首先输入符号变成了 双小于号( << )
    • 然后 EOF 实际上就是一个起始和结束的标识,两个标识之间被包裹的就是需要被输入到命令的内容
    • 标识的命令是完全自定义的,只需要保证首尾的标识是一致的即可被识别


11.6 管道

  1. 将一个命令的输出重定向到另一个命令,就是 管道连接( Piping ) ,如下图
    • set 命令会将当前系统的所有环境都输出,如果直接输出,通常会一口气显示很多内容
    • 现在则是通过 more -10 命令表示将结果使用分页显示,并且一页只显示 10 行
    • setmore -10 之间的 竖线( | ) ,就是用于 管道连接 的符号
  2. 被管道连接的两个命令,并不是依次执行的,而是同时执行

11.7 执行数学运算

  1. 不论是使用 expr 命令进行运算,还是使用 $[] 符号进行运算,shell 脚本原生的运算方式都 只支持整数运算

11.7.1 expr 命令

  1. 使用 expr 命令可以实现在命令行进行数学运算,但不是很好用,如下图
    • 两个待运算的值与运算符之间必须使用空格,否则无法被识别
    • 有些运算符还涉及到了关键字,导致使用时必须使用反斜线来进行转义,否则会出现语法错误


  2. 如果在 shell 脚本中使用 expr 命令进行运算的话,还必须使用 命令替换 的格式将 epxr 命令的表达式进行包裹,如下图

11.7.2 使用方括号

  1. 在 shell 脚本中进行数学运算时,可以使用 $[arg1 + arg2] 的方式,如下图
    • 可以看到,这种方式要比使用 expr 命令方便的多,而且也不会出现运算符是关键字的问题

11.7.3 浮点的解决方案

  1. 为了解决 shell 脚本原生的运算方式只支持整数运算的问题,Linux 为 shell 内建了一个名为 bc 的计算器

11.7.3.1 bc 的基本用法

  1. 输入 bc 命令可以直接进入计算器模式,如下图
    • 可以看到,在该模式下,输出待计算的公式后按回车即可得到结果
    • 而是是支持浮点运算的
    • 退出方式则是输入 quit 后再按回车
  2. 输入 bc -q 命令可以再进入计算器的同时忽略欢迎信息,如下图
  3. 在计算器模式中,还可以通过 scale 选项设置两数相除后小数点后保留精确度,比如 scale=4 就表示保留小数点后四位,如下图
    • 需要注意的是,只影响除法


  4. 在计算器模式中,不光可以直接进行数学运算,还支持注释、表达式、编程语句以及函数,如下图
    • 在计算器中声明了一个变量,再通过 print 命令将变量值输出

11.7.3.2 在脚本中使用 bc

  1. 实现的方式就是结合上文中学到的知识点,使用 命令替换echo 命令以及 管道连接 来获取 bc 的计算结果,如下图
  2. 如果觉得将多个命令及参数都写在同一行不易于理解,还可以结合 内嵌输入重定向 将命令和参数进行换行,如下图
  3. 需要注意的是,在 bc 计算器中使用的变量,和 shell 脚本不是同一个作用域,所以在 bc 中定义的变量无法在 shell 脚本中引用

11.8 退出脚本

  1. shell 中运行的每个命令在运行完毕后都会输出一个 退出状态码( Exit Status )
  2. 退出状态码 是一个 0-255 的整数值,可以在 shell 脚本中捕获命令的退出状态码,从而得知命令的执行结果

11.8.1 查看退出状态码

  1. 使用 $? 命令可以查看上一个命令执行结束后的 退出状态码 ,如下图
    • 使用 ls 命令输出当前的目录的内容列表后,再使用 $? 命令得到的 退出状态码 是 0
    • 使用 nano 命令输出的结果是未找到命令( 因为当前系统没有安装 nano 编辑器 ),再使用 $? 命令得到的 退出状态码 是 127
  2. 下图列出一些常见的 退出状态码

11.8.2 exit 命令

  1. 使用 exit 命令,可以再 shell 脚本结束时指定一个状态码,如下图
  2. exit 命令后的数字还可以使用变量值,如下图
  3. 但是指定的值必须是 0-255 之间的数值,如果超过 255 ,则会使用 指定的值除以 256 之后得到的余数 ,如下图
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容