当前位置:   article > 正文

Linux操作系统实验五 shell编程基础_请在/home/学号/test目录中用shell写一个简短的shell程序,文件名为demo.sh,

请在/home/学号/test目录中用shell写一个简短的shell程序,文件名为demo.sh,实现1+

1.实验目的与要求

  1. Shell特殊字符的使用
  2. Shell变量和运算符
  3. Shell输入输出语句
  4. Shell函数定义和调用

2.实验平台

实验室安装的实验环境(Linux操作系统)和头歌(www.educoder.net)实验平台(课程实验)

3.实验内容

  1. 练习Shell特殊字符的使用
  2. 练习Shell变量和字符串
  3. 练习Shell函数的使用

4.实验详细内容、步骤

任务描述

本关任务:欢迎来到shell 脚本世界,为了见证shell脚本的神奇,本小节带领大家写第一个shell脚本,希望脚本能够在右侧的命令行窗口中输出hello EduCoder

接下来让我们一起去开启shell脚本的快乐之旅吧!

相关知识

Shell是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。为了更好地学习shell编程,您需要对linux 系统命令有一定的了解,熟悉常用的命令,如:vim、echo、chmod等。

shell 语言编写的程序通常都非常轻巧,比如我想编写一个在屏幕上显示Hello World !程序:

  1. #!/bin/bash
  2. echo "Hello World !"

说明:#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 shellecho 命令用于向窗口输出文本内容。

Shell 脚本的运行通常有两种方法: 1、作为可执行程序运行

  1. chmod +x ./test.sh  #使脚本具有执行权限
  2. ./test.sh  #执行脚本

注意:一定要写成 ./test.sh,而不是 test.sh./的作用其实就是告诉系统就在当前目录查找,否则系统会去PATH路径中查找有没有test.sh的文件,而系统默认情况下只有 /bin, /sbin, /usr/bin/usr/sbin 等在 PATH 中,很难找到。

2、作为解释器参数运行,这种运行方式是直接运行解释器,其参数就是 shell 脚本的文件名,如:

  1. /bin/sh test.sh

这种方式运行的脚本,不需要在第一行指定解释器信息。

编程要求

请按照要求,完成以下实践内容。 1、在/opt/目录下创建第一个shell脚本 文件命名为 test.sh; 2、编写test.sh脚本,让其输出Hello EduCoder(通过 vim 编辑器编辑); 3、给/opt/test.sh赋予执行权限; 4、运行test.sh文件。

任务描述

本关带领大家熟悉 shell 的变量并掌握其使用。

变量概念

    从字面上来看就是可以变的量,举个例子,我们小时候都做过数学的应用题,经常定义 y 的值是某个数,如果换了一道题,还是定义 y 的值,但是 y 的值就不和第一道题相同了,这个 y 就是变量。

    变量是计算机内存的单元,在shell中变量其中存放的值可以改变。当 Shell 脚本需要保存一些信息时,如一段字符串或者一段数据,我们可以通过变量赋值的方式把他保存在变量中。

    每个变量都有一个名字,所以命令变量,尽量做到,见名知义。     那么,在 shell 中应该如何设置变量呢?其实 so easy,命令如下:

  1. [root@localhost ~]# name=jerry
  2. #定义变量name的值
  3. [root@localhost ~]# echo $name
  4. jerry
  5. #查询变量的值
变量类型

    运行 shell 时,会同时存在三种变量:

  1. 局部变量: 局部变量在脚本或命令中定义,仅在当前 shell 实例中有效,其他 shell 启动的程序不能访问局部变量,通常用于函数本地。
    • i.  #local 关键字
    • ii.  local VAR_NAME=VALUE
  2. 环境变量: 所有的程序,包括 shell 启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候 shell 脚本也可以定义环境变量 。
    • i. ##对当前shell进程及其子shell有效,对其它的shell进程无效  
    • ii. 定义:export VAR_NAME=VALUE
    • iii. 导出:export VAR_NAME
    • iv. 撤消变量:unset VAR_NAME
    • v. 只读变量:readonly VAR_NAME
  3. shell变量: shell变量是由 shell 程序设置的特殊变量。shell 变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了 shell 的正常运行      位置变量:用来接受变量指定位置的参数
    • i.   $1,$2...,${10}
    • ii.   ## 如下执行脚本a.sh
    • iii.   bash a.sh 1 2 3
    • iv.   ## $1即为第一个参数1  $2及为第二个参数 2

     特殊变量:shell 对一些参数做特殊处理,这些参数只能被引用而不能被赋值。

    • v. $#  传递到脚本的参数个数
    • vi. $*  显示所有向脚本传递的参数                  #与位置变量不同,此选项参数可超过9个
    • vii. $$  获取当前shell的进程号
    • viii. $!  执行上一个指令的进程号
    • ix. $?  获取执行的上一个指令的返回值              #0为执行成功,非零为执行失败
    • x. $-  显示shell使用的当前选项,与set命令功能相同
    • xi. $@  与$*相同,但是使用时加引号,并在引号中返回每个参数
变量定义规范

     1)在定义变量时,有一些规则需要遵守。

变量名可以由字母、数字和下画线组成,但是不能以数字开头,不能使用程序中的关键字(保留字),要见名知义。如果变量名是"20name或者if",则是错误的。

     2)在 Bash 中,变量的默认类型都是字符串型,如果要进行数值运算,则必须指定变量类型为数值型。比如:

  1. [root@localhost ~]# aa=1+2
  2. [root@localhost ~]# echo $aa
  3. 1+2

     看到了吧,变量 aa 的值不是"3",而是"1+2"。在 Bash 中,变量类型是字符串型,所以认为"1+2"只是一个字符串,而不会进行数值运算(数值运算方法会在后续章节中介绍)。

     3) 变量用等号"="连接值,"="左右两侧不能有空格。这是 Shell 语言特有的格式要求。在绝大多数的其他语言中,"="左右两侧是可以加入空格的。但是在 Shell 中命令的执行格式是"命令 [选项] [参数]",如果在"="左右两侧加入空格,那么 Linux 会误以为这是系统命令,是会报错的。

     4) 变量值中如果有空格,则需要使用单引号或双引号包含,如 test="hello world!"。双引号括起来的内容"$"和反引号者都拥有特殊含义,而单引号括起来的内容都是普通字符。

     5) 在变量值中,可以使用转义符"\"

     6) 如果需要増加变量值,那么可以进行变量叠加。 例如:

  1. [root@localhost ~]#test=123
  2. #叠加变量test,变量值变成了123456
  3. [root@localhost ~]#test=${test}456
  4. [root@localhost ~]# echo $test
  5. 123456

     7) 如果要把命令的执行结果作为变量值赋予变量,则需要使用反引号或 $() 包含命令。例如:

  1. ##$()引用执行结果
  2. [root@localhost ~]# test=$(date)
  3. [root@localhost ~]# echo $test
  4. Tue Feb 4 14:50:45 CST 2020
  5. ##反引号 引用执行结果
  6. root@Test-old-web01:~# test=`date`
  7. root@Test-old-web01:~# echo $test
  8. Tue Feb 4 14:51:19 CST 2020

     8) 环境变量名建议大写,便于区分。

删除变量

    使用 unset 命令可以删除变量。语法:

  1. unset variable_name

    变量被删除后不能再次使用。unset 命令不能删除只读变量(readonly myUrl 关键字 readonly 定义的变量叫做只读变量),如下:

  1. #!/bin/sh
  2. myUrl="http://www.educoder.net"
  3. unset myUrl
  4. echo $myUrl
  5. ##输出为空,是因为myurl已经被删除了
实践要求
  1. 创建一个脚本 /opt/test2.sh , 已知向 /opt/test.sh 传递参数 "www" "educoder" "net" 三个字符串。
  2. /opt/test.sh 需要实现如下功能:输出传递的参数的个数,并且输出第一个参数。

比如: 向 /opt/test2.sh传递 是"a" "b" "c" "d",则执行 bash /opt/test.sh a b c d 输出的结果应该是 4 a

任务描述

本关带领大家熟悉 shell 的变量并掌握其使用。

相关知识
字符串概念

     字符串是 shell 编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。

单引号 双引号 反引号的区别

     双引号 中可以有变量; 双引号里可以先转义字符;双引号中的单引号输出时维持单引号不变。请下面的案例: 例 1:

  1. #! /bin/bash
  2. var="aaa
  3. bbb
  4. ccc"
  5. echo ${var}
  6. ##输出结果为
  7. aaa bbb ccc

例 2:

  1. #! /bin/bash
  2. var="aaa
  3. bbb
  4. ccc"
  5. echo "${var}"
  6. ##输出结果为
  7. aaa
  8. bbb
  9. ccc

     单引号: 会忽略所有的特殊字符,即任何字符都会原样输出,包括定义的变量; 单引号字串中不能出现单引号。

  1. #! /bin/bash
  2. var="aaa
  3. bbb
  4. ccc"
  5. echo '${var}'
  6. ##输出结果为
  7. ${var}
  8. ###单引号无法解析变量,只会原样输出

    反引号 :  有命令替换的作用见例 3;反引号可以嵌套使用,但内层的单引号必须加上\ 进行转义见例 4。 例 3:

  1. $ echo the date is `date`
  2. the date is Tue Feb 4 18:08:12 CST 2020
  3. #这里的反引号 `date` 及为命令"date" 的结果,因此像引用一个命令返回的结果作为变量用 反引号 替换,也可以使用 $(date)的方式来替换使用命令结果

例 4:

  1. $ abc=`echo The number of users is \`who| wc -l\``
  2. $ echo $abc
  3. The number of users is 4
  4. ## 这里 `who|wc -l` 是指 返回的正在登录系统的用户的个数的结果 为4
  5. ## 因为反引号嵌套了反引号,所以在需要加上\转义
字符串常见操作表达式

 示例:

获取字符串长度

  1. string="abcdefg"
  2. echo ${#string}

字符串截取

  1. ${string:position}    //在$string中, 从位置$position开始提取子串
  2. ${string:position:length}    //在$string中, 从位置$position开始提取长度为$length的子串
  3. 测试例子
  4. string="abc12342341"
  5. echo ${string:4}      //2342341  从第4位开始截取后面所有字符串   
  6. echo ${string:3:3}    //123      从第3位开始截取后面3位   
  7. echo ${string:3:6}    //123423   从第3位开始截取后面6位   
  8. echo ${string: -4}    //2341  :右边有空格   截取后4位   
  9. echo ${string:(-4)}   //2341  同上
  10. echo ${str:(-6):5}    //34234 从倒数第6个位置向左提取5个字符字符串,

字符串匹配删除

  1. ${string#substring}     //从变量$string的开头, 删除最短匹配$substring的子串
  2. ${string##substring}    //从变量$string的开头, 删除最长匹配$substring的子串
  3. ${string%substring}     //从变量$string的结尾, 删除最短匹配$substring的子串
  4. ${string%%substring}    //从变量$string的结尾, 删除最长匹配$substring的子串
  5. 测试例子
  6.     test='c:/windows/boot.ini'
  7.     $ echo ${test#/} (从头匹配斜杠/,删除匹配到最短的斜杠,没有匹配到,所以没有删除)
  8.     c:/windows/boot.ini
  9.     $ echo ${test#*/}  (删除 从头匹配删除匹配到最短以/结尾的字符串,*是匹配0个或者多个)
  10.     windows/boot.ini
  11.     $ echo ${test##*/} (删除 从头匹配匹配到最长的以/结尾的字符串,通常可以用来获取到文件名)
  12.     boot.ini
  13.     $ echo ${test%/*} (删除 从尾部匹配以/开始最短的字符串,通常可以获取到文件路径前缀)
  14.     c:/windows
  15.     $ echo ${test%%/*} (删除 从尾部匹配以/开始最长的字符串)
  16.     c:

字符串替换

  1. 表达式规则
  2. ${string/substring/replacement}         使用$replacement, 来代替第一个匹配的$substring
  3. ${string//substring/replacement}    使用$replacement, 代替所有匹配的$substring
  4. ${string/#substring/replacement}    如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
  5. ${string/%substring/replacement}    如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
  6. 测试列子:
  7. str="apple, tree, apple tree"
  8. echo ${str/apple/APPLE}   # 替换第一次出现的apple
  9. APPLE, tree, apple tree
  10. echo ${str//apple/APPLE}  # 替换所有apple
  11. APPLE, tree, APPLE tree
  12. echo ${str/#apple/APPLE}  # 如果字符串str以apple开头,则用APPLE替换它
  13. APPLE, tree, apple tree
  14. echo ${str/%apple/APPLE}  # 如果字符串str以apple结尾,则用APPLE替换它(str是以tree结尾的)
  15. apple, tree, apple tree
  16. 测试列子2:
  17. $ test='c:/windows/boot.ini' 
  18. $ echo ${test/\//\\}  (匹配的为\/,匹配的子串/需要转义,所以添加\,同理替换的字符串\转义为\\)
  19. c:\windows/boot.ini 
  20. $ echo ${test//\//\\} 
  21. c:\windows\boot.ini
  22. #${变量/查找/替换值} 一个“/”表示替换第一个,”//”表示替换所有,当查找中出现了:”/”请加转义符”\/”表示。

字符串比较     四种判断方式 == != < > (按 ascii 值比较大小), == 相等则为真,!= 不相等则为真。

  1. str="bacd"
  2. ##判断字符串是否相等 这里的== 也可以换做 =  但是一般情况下使用==
  3. if [ "$str"x == "abcdsd"x ]; then
  4.     echo "相等"
  5. fi
  6. ##不相等则为真
  7. if [ "$str"x != "abcdsd"x ]; then
  8.     echo "不相等"
  9. fi
  10. ##注意  比较字符串是否相等,可以字符串后面+上个别的字符,如果str为空的话,可以防止表达式报错:[: =: unary operator expected
实践要求

按照右侧“代码文件”中提示的要求补全代码,本关的目的,是希望通过学习,对于字符串的使用能有一个大致的了解,在实际工作中遇到需要对某一个字符串处理的时候,能够找到对应的方法。

任务描述

本关将带领大家熟悉 bash 的内置命令,并掌握一些常用的内置命令。     在开启此小节之前,相信你已经对 shell 编程的变量和字符串的使用有了一定的了解。需注意: 本课程没有描述 shell 数组的概念,因为在实际的操作中 shell 数组并不是很友好,一般在数据处理的模型都用 python 来代替 shell,如果大家对 shell 数组感兴趣,请同学们可以自行翻阅资料了解一下 shell 的数组概念。

相关知识
什么是内建命令

    所谓 Shell 内建命令,就是由 Bash 自身提供的命令,而不是文件系统中的某个可执行文件。     例如,用于进入或者切换目录的 cd 命令,虽然我们一直在使用它,但如果不加以注意,很难意识到它与普通命令的性质是不一样的:该命令并不是某个外部文件,只要在 Shell 中你就可以运行这个命令。     可以使用 type 来确定一个命令是否是内建命令:

  1. root@Test-old-web01:/opt/f89vxyw7/task1# type cd
  2. cd is a shell builtin
  3. root@Test-old-web01:/opt/f89vxyw7/task1# type ifconfig
  4. ifconfig is /sbin/ifconfig

    由此可见,cd 是一个 Shell 内建命令,而 ifconfig 是一个外部文件,它的位置是/sbin/ifconfig。     还记得系统变量 $PATH 吗?$PATH 变量包含的目录中几乎聚集了系统中绝大多数的可执行命令,它们都是外部命令。

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

shell有哪些内建命令

    下表列出了 Bash Shell 中直接可用的内建命令。

    命令 说明

  1. :    扩展参数列表,执行重定向操作
  2. .    读取并执行指定文件中的命令(在当前 shell 环境中)
  3. alias    为指定命令定义一个别名
  4. bg    将作业以后台模式运行
  5. bind    将键盘序列绑定到一个 readline 函数或宏
  6. break    退出 for、while、select 或 until 循环
  7. builtin    执行指定的 shell 内建命令
  8. caller    返回活动子函数调用的上下文
  9. cd    将当前目录切换为指定的目录
  10. command    执行指定的命令,无需进行通常的 shell 查找
  11. compgen    为指定单词生成可能的补全匹配
  12. complete    显示指定的单词是如何补全的
  13. compopt    修改指定单词的补全选项
  14. continue    继续执行 for、while、select 或 until 循环的下一次迭代
  15. declare    声明一个变量或变量类型。
  16. dirs    显示当前存储目录的列表
  17. disown    从进程作业表中刪除指定的作业
  18. echo    将指定字符串输出到 STDOUT
  19. enable    启用或禁用指定的内建shell命令
  20. eval    将指定的参数拼接成一个命令,然后执行该命令
  21. exec    用指定命令替换 shell 进程
  22. exit    强制 shell 以指定的退出状态码退出
  23. export    设置子 shell 进程可用的变量
  24. fc    从历史记录中选择命令列表
  25. fg    将作业以前台模式运行
  26. getopts    分析指定的位置参数
  27. hash    查找并记住指定命令的全路径名
  28. help    显示帮助文件
  29. history    显示命令历史记录
  30. jobs    列出活动作业
  31. kill    向指定的进程 ID(PID) 发送一个系统信号
  32. let    计算一个数学表达式中的每个参数
  33. local    在函数中创建一个作用域受限的变量
  34. logout    退出登录 shell
  35. mapfile    从 STDIN 读取数据行,并将其加入索引数组
  36. popd    从目录栈中删除记录
  37. printf    使用格式化字符串显示文本
  38. pushd    向目录栈添加一个目录
  39. pwd    显示当前工作目录的路径名
  40. read    从 STDIN 读取一行数据并将其赋给一个变量
  41. readarray    从 STDIN 读取数据行并将其放入索引数组
  42. readonly    从 STDIN 读取一行数据并将其赋给一个不可修改的变量
  43. return    强制函数以某个值退出,这个值可以被调用脚本提取
  44. set    设置并显示环境变量的值和 shell 属性
  45. shift    将位置参数依次向下降一个位置
  46. shopt    打开/关闭控制 shell 可选行为的变量值
  47. source    读取并执行指定文件中的命令(在当前 shell 环境中)
  48. suspend    暂停 Shell 的执行,直到收到一个 SIGCONT 信号
  49. test    基于指定条件返回退出状态码 0 或 1
  50. times    显示累计的用户和系统时间
  51. trap    如果收到了指定的系统信号,执行指定的命令
  52. type    显示指定的单词如果作为命令将会如何被解释
  53. typeset    声明一个变量或变量类型。
  54. ulimit    为系统用户设置指定的资源的上限
  55. umask    为新建的文件和目录设置默认权限
  56. unalias    刪除指定的别名
  57. unset    刪除指定的环境变量或 shell 属性
  58. wait    等待指定的进程完成,并返回退出状态码
常用的内建命令

     一下子掌握所有的内建命令难度要求比较大,我们可以从常用内建命令入手,第一个内置命令: alias。 1、alias      alias 用来给命令创建一个别名。若直接输入该命令且不带任何参数,则列出当前 Shell 进程中使用了哪些别名,如下:

  1. root@Test-old-web01:/opt/f89vxyw7/task1# alias
  2. alias egrep='egrep --color=auto'
  3. alias fgrep='fgrep --color=auto'
  4. alias grep='grep --color=auto'
  5. alias l='ls -CF'
  6. alias la='ls -A'a
  7. alias ll='ls -alF'
  8. alias ls='ls --color=auto'

     现在你应该能理解类似ll这样的命令为什么与ls -l或者 ls -alF的效果是一样的吧,是因为ls -alF 被重新命名为ll 。因此,在命令行输入 ll ,即相当于输入了 ls -alF

    使用 alias 命令自定义别名的语法格式为(临时):

  1.     alias new_name='command'

    通过 date 命令可以获得当前的 UNIX 时间戳,具体写法为 date +%s,如果你嫌弃它太长或者不容易记住,那可以给它定义一个别名。

  1.     alias timestamp='date +%s'

    这样你在命令行输入 timestamp获得的效果及跟输入date +%s一样,如下:

  1. root@Test-old-web01:/opt/f89vxyw7/task1# date +%s
  2. 1580972143
  3. root@Test-old-web01:/opt/f89vxyw7/task1# alias timestamp='date +%s'
  4. root@Test-old-web01:/opt/f89vxyw7/task1# timestamp
  5. 1580972158

    注意:通过以上方式命令别名后,在退出当前窗口后,刚设定的 alias 命令别名也就消失了,如何才能永久使用了?请看下面的介绍:

    首先,如果想让别名能够在不同的 shell 会话中使用,就需要把它们添加到 shell 用来保存别名的文件中。大多数情况下,这个文件不是 .bashrc,就是 .bash_aliases,我们使用的系统 CentOS,是保存在.bashrc里。(或者可以在 root 家目录下输入:ls -a ~看到那个文件)

    如果要永久保存,就把alias new_name='command'给编辑到 /root/.bashrc下,然后:wq保存退出,但得重新加载别名文件,才能生效,可执行 source ~/.bashrc (这样更改后只有 root 用户的别名生效,要想普通用户有效,普通用户目录下的.bashrc也必需作同样的修改!比如现在有用户为 jerry,则需要到其/home/jerry/.bashrc下编辑然后执行source ~/.bashrc )。

  1. ##pdl用户登录,在/home/pdl加目录下 或者 "~"及代表用户的家目录
  2. ## /home/pdl/.bashrc 某行添加 
  3. ## alias homeview='ll ~'
  4. ##添加
  5. pdl@Test-old-web01:~$ vim /home/pdl/.bashrc
  6. ##重新加载别名文件
  7. pdl@Test-old-web01:~$ source ~/.bashrc
  8. ##执行homeview 即相当于 执行 ll ~
  9. pdl@Test-old-web01:~$ homeview
  10. total 36
  11. drwx------ 4 pdl  pdl  4096 Feb  6 15:10 ./
  12. drwxr-xr-x 5 root root 4096 Dec 30 11:18 ../
  13. -rw------- 1 pdl  pdl  1114 Nov 21 16:54 .bash_history
  14. -rw-r--r-- 1 pdl  pdl   220 Sep  1  2015 .bash_logout
  15. -rw-r--r-- 1 pdl  pdl  3795 Feb  6 15:10 .bashrc
  16. drwx------ 2 pdl  pdl  4096 Aug 22 09:00 .cache/
  17. -rw-r--r-- 1 pdl  pdl   655 May 16  2017 .profile
  18. drwx------ 2 pdl  pdl  4096 Aug 22 15:11 .ssh/
  19. -rw------- 1 pdl  pdl  1016 Feb  6 15:10 .viminfo
  20. ##退出终端重新加载,发现别名homeview永久存在

    实际用例:假设生产环境有三台机器127.1.1.2127.1.1.3127.1.1.4 ; 你可以通过 alias 的方式简化 ssh 登录方式,操作如下:     在~/.bashrc下末尾行追加内容。然后 source ~/.bashrc ,这样即可以在命令行直接输入 ssh_01,ssh_02,ssh_03快速登陆。

  1. alias ssh_01= 'sshpass -p 密码 ssh -o StrictHostKeyChecking=no -p 22  root@127.1.2'
  2. alias ssh_02= 'sshpass -p pass2 ssh -o StrictHostKeyChecking=no -p 22  root@127.1.1.3'
  3. alias ssh_03= 'sshpass -p pass4 ssh -o StrictHostKeyChecking=no -p 22  root@127.1.1.4'
实践要求

     本关没有评测,希望大家独立在命令行按照示例自己操作一遍,了解熟悉 shell 的内置命令,并掌握 alias 的使用方式以及应用场景。

任务描述

    本关继续为大家介绍 shell 的内置命令 echo 和 read,通过学习掌握 echo 和 read 的用法。

相关知识
内置命令——echo

     echo 是一个 Shell 内建命令,用来在终端输出字符串,并在最后默认加上换行符。请看下面的例子:

  1. #!/bin/bash
  2. name="在线编程"
  3. url="www.educoder.net"
  4. echo "小哥哥,小姐姐,你好!"  #直接输出字符串
  5. echo $url  #输出变量
  6. echo "${name}的网址是:${url}"  #双引号包围的字符串中可以解析变量
  7. echo '${name}的网址是:${url}'  #单引号包围的字符串中不能解析变量

     输出结果为:

  1. [root@pre-host-work02 opt]# bash a.sh
  2. 小哥哥,小姐姐,你好!
  3. www.educoder.net
  4. 在线编程的网址是:www.educoder.net

    echo 命令输出结束后默认会换行,如果不希望换行,可以加上-n参数,如下所示:

  1. #!/bin/bash
  2. name="在线编程"
  3. url="www.educoder.net"
  4. echo -n "小哥哥,小姐姐,你好!"  #直接输出字符串
  5. echo -n $url  #输出变量
  6. echo  "${name}的网址是:${url}"  #双引号包围的字符串中可以解析变量

  输出结果为:

  1. [root@pre-host-work02 opt]# bash a.sh
  2. 小哥哥,小姐姐,你好!www.educoder.net在线编程的网址是:www.educoder.net

     默认情况下,echo 不会解析以反斜杠\开头的转义字符。比如,\n表示换行,echo 默认会将它作为普通字符对待。请看下面的例子:

  1. [root@localhost ~]# echo "hello \nworld"
  2. hello \nworld

    我们可以添加-e参数来让 echo 命令解析转义字符。例如:

  1. [root@localhost ~]# echo -e "hello \nworld"
  2. hello
  3. world
内置变量——read

    read 是 Shell 内置命令,用来从标准输入中读取数据并赋值给变量。如果没有进行重定向,默认就是从键盘读取用户输入的数据;如果进行了重定向,那么可以从文件中读取数据(后续我们会在 shell 的输入和输出中深入讲解重定向的概念,不了解的同学可以不用理会,暂时就认为:read 命令就是从键盘读取数据)。
    read 命令的用法为:

  1.     read [-options] [variables]

    options 表示选项,如下表所示;variables 表示用来存储数据的变量,可以有一个,也可以有多个。

    options 和 variables 都是可选的,如果没有提供变量名,那么读取的数据将存放到环境变量 REPLY 中。     Shell read 命令支持的选项:

    【实例1】使用 read 命令给多个变量赋值,并打印提示语 read -p

  1. #!/bin/bash
  2. read -p "Enter some information > " name age
  3. echo "网站名字:$name"
  4. echo "年龄:$age"

    输出结果为:

  1. [root@pre-host-work02 opt]# bash a.sh
  2. Enter some information > educoder 20
  3. 网站名字:educoder
  4. 年龄:20

    Tips: 如果用 read 交互参数,输入错了,请按住Ctrl+Backspace清除而不是只按住Backspace。

    【示例2】只读取一个字符。

  1. #!/bin/bash
  2. read -n 1 -p "Enter a char > " char
  3. printf "\n"  #换行
  4. echo $char

    运行结果:

  1.     Enter a char > 1
  2.     1

    【实例3】在指定时间内输入密码。

  1. #!/bin/bash
  2. if
  3.     read -t 20 -sp "Enter password in 20 seconds(once) > " pass1 && printf "\n" &&  #第一次输入密码
  4.     read -t 20 -sp "Enter password in 20 seconds(again)> " pass2 && printf "\n" &&  #第二次输入密码
  5.     [ $pass1 == $pass2 ]  #判断两次输入的密码是否相等
  6. then
  7.     echo "Valid password"
  8. else
  9.     echo "Invalid password"
  10. fi

    这段代码中,我们使用**&&**组合了多个命令,相当于其他语言中的 and ,这些命令会依次执行,并且从整体上作为 if 语句的判断条件。只要其中一个命令执行失败(退出状态为非 0 值),整个判断条件就失败了,后续的命令也就没有必要执行了。

  1. 如果两次输入密码相同,运行结果为:
  2. Enter password in 20 seconds(once) >
  3. Enter password in 20 seconds(again)>
  4. Valid password
  5. 如果两次输入密码不同,运行结果为:
  6. Enter password in 20 seconds(once) >
  7. Enter password in 20 seconds(again)>
  8. Invalid password
  9. 如果第一次输入超时,运行结果为:
  10. Enter password in 20 seconds(once) > Invalid password
  11. 如果第二次输入超时,运行结果为:
  12. Enter password in 20 seconds(once) >
  13. Enter password in 20 seconds(again)> Invalid password

    关于 echo 和 read 的使用方式还有很多,大家可以通过 help echo 和 help read 的方式去获取更多的帮助文档。

任务描述

本关继续带领大家了解掌握 shell 的内置命令,接下来将会学习内置命令exitdeclare

相关知识
内置命令 —— exit

exit 是一个 Shell 内置命令,用来退出当前 Shell 进程,并返回一个退出状态;使用**$?**可以接收这个退出状态,$?如果不记得是什么,请查看前一章节的shell变量。exit 命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。

    一般情况下,退出状态为 0 表示成功,退出状态为非 0 表示执行失败(出错)了, exit 退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。

    Shell 进程执行出错时,可以根据退出状态来判断具体出现了什么错误,比如打开一个文件时,我们可以指定 1 表示文件不存在,2 表示文件没有读取权限,3 表示文件类型不对。     【实列1】运行该脚本:

  1. #!/bin/bash
  2. echo "befor exit"
  3. exit 8
  4. echo "after exit
  1. [root@localhost ~]$ bash ./test.sh
  2. befor exit

    可以看到,"after exit"并没有输出,这说明遇到 exit 命令后,test.sh 执行就结束了。     注意:exit 表示退出当前 Shell 进程,我们必须在新进程中运行 test.sh,否则当前 Shell 会话(终端窗口)会被关闭,我们就无法看到输出结果了。     我们可以紧接着使用$?来获取 test.sh 的退出状态:

  1.     [root@localhost ~]$ echo $?
  2.     8
内置命令 —— declare

declare一般很少用到,但大家要知道下列常用的方法。

【实列2】declare 将变量声明为整数并进行计算。

  1. #!/bin/bash
  2. declare -i m n ret  #将多个变量声明为整数
  3. m=10
  4. n=30
  5. ret=$m+$n
  6. echo $ret

    如果我们不使用 declare,结果如下所示:

  1. [root@pre-host-work02 ~]# a=10
  2. [root@pre-host-work02 ~]# b=20
  3. [root@pre-host-work02 ~]# c=$a+$b
  4. [root@pre-host-work02 ~]# echo $c
  5. 10+20

    因此注意在 shell 如果要求整数值的话,不要忘了有 declare 这样的内置命令来定义变量。关于 declare 的其他使用方式,大家可以自行去了解。

编程要求

补全右侧编辑器 Begin-End 区间的代码,完成指定功能,具体要求如下:

1.现有变量 a、b、c ,已知 a 为整数 100,b 为整数 300,c=$a+$b,打印 c 的值,应该为 400; 2.补全[ $? -eq ]内的值,-eq 的意思是‘等于’,注意 [ ] 两边要留一个空格,通过 $? 判断上面的命令是否运行成功,如果运行成功则输出success ,否则输出 faild

注意:请不要直接输出 400 或者直接输出 success,因为评测程序会判断你的代码文件。

任务描述

本关任务:掌握函数的创建和使用方法。

相关知识

函数是一个脚本代码块,你可以为其命名并在代码中任何位置重用。要在脚本中使用该代码块时,只要使用所起的函数名就行了(这个过程称为调用函数)。本关将会介绍如何在 shell 脚本中创建和使用函数。

创建函数

有两种格式可以用来在 bash shell 脚本中创建函数。

  • 第一种格式采用关键字 function,后跟分配给该代码块的函数名。
  1. function name {
  2.  commands
  3. }

参数说明:

  1. name 属性定义了赋予函数的唯一名称。脚本中定义的每个函数都必须有一个唯一的名称。
  2. commands 是构成函数的一条或多条 bash shell 命令。在调用该函数时,bash shell 会按命令在函数中出现的顺序依次执行,就像在普通脚本中一样。
  • 定义函数的第二种格式更接近于其他编程语言中定义函数的方式。
    • i. name() {
    • ii. commands
    • iii. }

函数名后的空括号表明正在定义的是一个函数。这种格式的命名规则和之前定义 shell 脚本函数的格式一样。

使用函数

【实例1】要在脚本中使用函数,只需要像其他 shell 命令一样,在行中指定函数名就行了。 已知 cat test.sh 显示内容如下:

  1. #!/bin/bash
  2. # using a function in a script
  3. function func1 {
  4.  echo "This is an example of a function"
  5. }
  6. count=1
  7. while [ $count -le 5 ]
  8. do
  9.  func1
  10. count=$[ $count + 1 ]
  11. done
  12. echo "This is the end of the loop"
  13. func1                             # 指定函数名即可调用函数
  14. echo "Now this is the end of the script"

执行输出结果如下:

  1. This is an example of a function
  2. This is an example of a function
  3. This is an example of a function
  4. This is an example of a function
  5. This is an example of a function
  6. This is the end of the loop
  7. This is an example of a function
  8. Now this is the end of the script

每次引用函数名 func1 时,bash shell 会找到 func1 函数的定义并执行你在那里定义的命令。 【实例2】函数的调用要在函数定义之后使用,否则会报错。

  1. #!/bin/bash
  2. echo "hello function"
  3. fun1
  4. fun1(){
  5. echo "i am func1"
  6. }

执行结果如下:

  1. # bash a.sh
  2. hello function
  3. a.sh: line 6: fun1: command not found

【实例3】函数名必须是唯一的,否则也会有问题。如果你重定义了函数,新定义会覆盖原来函数的定义,这一切不会产生任何错误消息。

  1. #!/bin/bash
  2. fun1(){
  3. echo "i am func1"
  4. }
  5. fun1(){
  6. echo "i am new func1"
  7. }
  8. fun1

执行结果如下:

  1. i am new func1
编程要求

在右侧编辑器 Begin-End 区间补充代码,调用 welcome 函数输出指定内容。

任务描述

本关任务:学习掌握什么是 shell 函数的返回值。

相关知识

bash shell 会把函数当作一个小型脚本,运行结束时,会返回一个退出状态码。有 3 种不同的方法来为函数生成退出状态码。

默认退出状态码

默认情况下,函数的退出状态码是函数中最后一条命令返回的退出状态码。在函数执行结束后,可以用标准变量$?来确定函数的退出状态码。 【实例1】已知 test1.sh 脚本内容如下:

  1. #!/bin/bash
  2. func1() {
  3.  echo "trying to display a non-existent file"
  4.  ls -l badfile
  5. }
  6. echo "testing the function: "
  7. func1
  8. echo "The exit status is: $?"

运行脚本 bash test1.sh 执行结果如下:

  1. testing the function:
  2. trying to display a non-existent file
  3. ls: cannot access badfile: No such file or directory
  4. The exit status is: 2

函数的退出状态码是 2,这是因为函数中的最后一条命令没有成功运行。但你无法知道函数中其他命令中是否成功运行。带着这个疑问再看下面的例子:

【实例2】已知 test2.sh 脚本内容如下:

  1. #!/bin/bash
  2. func1() {
  3.  ls -l badfile
  4.  echo "trying to display a non-existent file"
  5. }
  6. echo "testing the function: "
  7. func1
  8. echo "The exit status is: $?"

运行脚本 bash test1.sh 执行结果如下:

  1. testing the function:
  2. ls: cannot access badfile: No such file or directory
  3. trying to display a non-existent file
  4. The exit status is: 0

这次,由于函数最后一条语句 echo 运行成功,该函数的退出状态码就是 0,尽管其中有一条命令并没有正常运行。由此可见,使用函数的默认退出状态码,来判断函数是否正常运行是很危险的。幸运的是,有几种办法可以解决这个问题。

return 命令

bash shell 使用 return 命令退出函数,并返回特定的退出状态码。return 命令允许指定一个整数值来定义函数的退出状态码,从而提供了一种简单的途径来设定函数退出状态码。 【实例3】已知 test3.sh 脚本的内容如下:

  1. #!/bin/bash
  2. func1() {
  3.  ls -l badfile
  4.  echo "trying to display a non-existent file"
  5.  return 20
  6. }
  7. echo "testing the function: "
  8. func1
  9. echo "The exit status is: $?"

执行bash test.sh显示如下:

  1. testing the function:
  2. ls: cannot access badfile: No such file or directory
  3. trying to display a non-existent file
  4. The exit status is: 20

这一次变量$?返回的结果是 20,而按照默认的返回 $? 应该是 0,因为函数的最后一条命令正确执行。

任务描述

本关任务:掌握函数传递参数的方法以及如何处理变量。

相关知识
向函数传递参数

我们在 shell 变量那个实训,介绍过了一些特殊变量 $0$1$# 等。在 shell 中,函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在$0变量中定义,函数命令行上的任何参数都会通过$1$2等定义。也可以用特殊变量$#,来判断传给函数的参数数目。请参考实例 1:

【实例1】已知脚本 test.sh 的内容如下:

  1. function addem {
  2.  if [ $# -eq 0 ] || [ $# -gt 2 ]   # 判断传递给函数的参数个数
  3.      then
  4.      echo -1
  5.  elif [ $# -eq 1 ]
  6.      then
  7.      echo $[ $1 + $1 ]
  8.  else
  9.      echo $[ $1 + $2 ]
  10. fi
  11. }
  12. echo -n "Adding 10 and 15: "
  13. value=$(addem 10 15)
  14. echo $value
  15. echo -n "Let's try adding just one number: "
  16. value=$(addem 10)
  17. echo $value
  18. echo -n "Now trying adding no numbers: "
  19. value=$(addem)
  20. echo $value
  21. echo -n "Finally, try adding three numbers: "
  22. value=$(addem 10 15 20)
  23. echo $value

输出结果如下:

  1. Adding 10 and 15: 25
  2. Let's try adding just one number: 20
  3. Now trying adding no numbers: -1
  4. Finally, try adding three numbers: -1

这段脚本的意思是:脚本中的 addem 函数首先会检查脚本传给它的参数数目。如果没有任何参数,或者参数多于两个,addem 会返回值 -1;如果只有一个参数,addem 会将参数与自身相加;如果有两个参数,addem 会将它们进行相加。

注意:由于函数使用特殊参数环境变量作为自己的参数值,因此它无法直接获取脚本在命令行中的参数值。下面的【实例2】将会运行失败。

【实例2】:已知 test2.sh 的内容如下:

  1. function badfunc1 {
  2.      echo $[ $1 * $2 ]
  3. }
  4. badfunc1

我在命令行执行 bash test2.sh 10 3,发现脚本报错:

  1. [root@pre-host-work02 opt]# bash a.sh 1 10
  2. a.sh: line 3: *  : syntax error: operand expected (error token is "*  ")

尽管函数 badfunc1 也使用了 $1 和 $2 变量,但它们和脚本主体中的 $1 和 $2 变量并不相同。要在函数中使用这些值,必须在调用函数时手动将它们传过去。我们把脚本改动如下:

  1. function badfunc1 {
  2.      echo $[ $1 * $2 ]
  3. }
  4. badfunc1 $1 $2     

通过将$1$2变量传给函数,它们就能跟其他变量一样供函数使用了,我在命令行执行 bash test2.sh 10 3,结果符合预期:

  1. [root@pre-host-work02 opt]# bash a.sh 10 3
  2. 30
函数中处理变量

函数使用两种类型的变量:

  • 全局变量
  • 局部变量
全局变量

全局变量是在 shell 脚本中任何地方都有效的变量。如果你在脚本的主体部分定义了一个全局变量,那么可以在函数内读取它的值。类似地,如果你在函数内定义了一个全局变量,可以在脚本的主体部分读取它的值。 默认情况下,你在脚本中定义的任何变量都是全局变量。在函数外定义的变量可在函数内正常访问,请看【实例3】。

【实列3】查看 test3.sh 内容,执行cat test3.sh 得到如下内容:

  1. #!/bin/bash
  2. function dbl {
  3.  value=$[ $value * 2 ]
  4. }
  5. value=450
  6. dbl
  7. echo "The new value is: $value"

执行脚本输出结果如下:

  1. The new value is: 900

$value变量在函数外定义并被赋值。当 dbl 函数被调用时,该变量及其值在函数中都依然有效。如果变量在函数内被赋予了新值,那么在脚本中引用该变量时,新值也依然有效。但这其实很危险,尤其是如果你想在不同的 shell 脚本中使用函数的话。它要求你清清楚楚地知道函数中具体使用了哪些变量,包括那些用来计算非返回值的变量。请看【实例4】。

【实例4】:

  1. #!/bin/bash
  2. function func1 {
  3.      temp=$[ $value + 5 ]
  4.      result=$[ $temp * 2 ]
  5. }
  6. temp=4
  7. value=6
  8. func1               # 这个时候全局变量temp=6+5及为11,result=22
  9. echo "The result is $result"
  10. if [ $temp -gt $value ]
  11. then
  12. echo "temp is larger"
  13. else
  14. echo "temp is smaller"
  15. fi

执行脚本输出结果如下:

  1. The result is 22
  2. temp is larger
局部变量

无需在函数中使用全局变量,函数内部使用的任何变量都可以被声明成局部变量。要实现这一点,只要在变量声明的前面加上 local 关键字就可以了,比如local temp。或者也可以在变量赋值语句中使用 local 关键字:local temp=$[ $value + 5 ]。 local 关键字保证了变量只局限在该函数中。如果脚本中在该函数之外有同样名字的变量,那么 shell 将会保持这两个变量的值是分离的。现在你就能很轻松地将函数变量和脚本变量隔离开了,只共享需要共享的变量。

【实例5】:

  1. #!/bin/bash
  2. # demonstrating the local keyword
  3. function func1 {
  4.      local temp=$[ $value + 5 ]
  5.      result=$[ $temp * 2 ]
  6. }
  7. temp=4                    
  8. value=6    
  9. func1         #这个时候全局部变量temp=6+5,但是全局变量temp=4.
  10. echo "The result is $result"
  11. if [ $temp -gt $value ]
  12. then
  13. echo "temp is larger"
  14. else
  15. echo "temp is smaller"
  16. fi

执行脚本输出结果如下:

  1. The result is 22
  2. temp is smaller

现在,在 func1 函数中使用 $temp 变量时,并不会影响在脚本主体中赋给$temp变量的值。请重点分析【实例4】和【实例5】中的不同。

编程要求

本关不进行考核,希望大家能够真正的理解:

  • 向函数传递变量的方法;
  • 在函数中使用局部变量和全局变量的区别。 这两点非常重要,希望大家能够在命令行中自行练习本关【实例】中代码,能够充分的理解掌握这两点内容。
任务描述

本关任务:掌握文件包含方法的使用。

相关知识
文件包含方法

在 C,C++,PHP 中都是用 include 来包含文件,Go 和 Java 使用 import 来包含(导入)包,而在 shell 中,很简单,只需要一个点.,然后跟着文件路径及文件名,或者使用source关键字也可以,注意文件路径可以使用绝对路径和相对路径。请看实例 1。

【实例1】已知在同一目录下,有 test1.sh 和 test2.sh 两个脚本,test1.sh 内容如下:

  1. #!/bin/bash
  2. url="www.educoder.net"

同一目录下的 test2.sh 内容如下:

  1. #!/bin/bash
  2. . ./test1.sh   # 第一个.是包含符号,第二个. 表示当前目录下。
  3.                # 这是采用相对路径包含的学法,记得中间要空格。
  4. echo "编程平台地址是:$url"

在命令行执行bash test2.sh,大家想想看这个时候输出的是什么? 输出的结果如下:

  1. 编程平台地址是:www.educoder.net

注意:

  • 变量的定义也要考虑局部变量和全局变量;
  • 如实例1 所示 test1.sh 并不需要赋予执行权限;
  • 注意包含文件的路径。

【实例2】同一目录下 test1.sh、test2.sh、test3.sh 内容如下:

  1. [root@pre-host-work02 opt]# cat test1.sh
  2. #!/bin/bash
  3. test(){
  4.     url='www.educoder.net'
  5. }
  1. [root@pre-host-work02 opt]# cat test2.sh
  2. #!/bin/bash
  3. hello(){
  4.     echo "i am from test3.sh"
  5. }
  1. [root@pre-host-work02 opt]# cat test3.sh
  2. #!/bin/bash
  3.                ##包含多个文件时,一行智能写一个文件,分开写。
  4. . ./test1.sh   ##包含test1.sh  ./test1.sh表示当前目录下
  5. . ./test2.sh   ##包含test2.sh  ./test2.sh表示当前目录下
  6. echo "编程平台:$url"
  7. hello

在命令行执行bash test3.sh ,输出结果如下:

  1. [root@pre-host-work02 opt]# bash test3.sh
  2. 编程平台:
  3. i am from test3.sh
  • 因为在 test1.sh 中 url 变量定义在函数内,即为局部变量,所以包含过来之后不能被调用;
  • test2.sh 中定义了函数 hello,在 test3.sh 通过的方式包含进来,通过函数名 hello 的调用,调用了 test2.sh 中的 hello()函数,输出“i am from test3.sh” 。
编程要求

本关任务包含了三个脚本文件,它们分别是 step4/t1.shstep4/t2.shstep4/main.sh,点击右侧“代码文件”即可查看,如下图所示:

  • 根据要求补全 step4/t1.sh 代码。
  • 根据要求补全 step4/main.sh 代码。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/197379
推荐阅读
相关标签
  

闽ICP备14008679号