2021-2022-diocs-sh编程(第三周学习笔记)

20191218 2021-2022-diocs-sh编程(第三周学习笔记)

一、任务详情

  • 自学教材第10章,提交学习笔记(10分)
    大家学习过Python,C,Java等语言,总结一下一门程序设计语言有哪些必备的要素和技能?这些要素和技能在shell脚本中是如何呈现出来的?
    • 知识点归纳以及自己最有收获的内容 (3分)
    • 问题与解决思路(2分)
    • 实践内容与截图,代码链接(3分)
    • ...(知识的结构化,知识的完整性等,提交markdown文档,使用openeuler系统等)(2分)

二、学习程序设计语言必备的要素和技能

结合自己学习编程语言的心路历程以及网上一些高级程序员总结的帖子,我觉得学习一门程序设计语言需要具备如下要素和能力:

  1. 抽象问题的能力
    从大一刚开始接触编程开始,娄老师就经常在课堂上和我们强调抽象在整个计算机行业的重要性。从最底层的晶体管到呈现在我们眼前一台完备的电脑,很多地方都用到了抽象。所谓抽象其实可以说就是数据建模的能力,即能把一个问题抽象或归类为某种方案来解决。如果我们实现了一个较底层的模块,像二进制加法,我们可以把这个模块就抽象成一个函数,其中代码是如何实现的就可以不必关注,我们只需要往里面提供参数就可以实现功能,并且以此为基础,我们可以拓展出乘法、除法等更多的模块。抽象层次越高,接口的语意就越模糊,适用的范围就越广,到最后就会变成数学模型或者概念。
    件设计和开发就是把现实中的问题映射的计算机的语言实现,但现实问题太复杂,细节太多,而且在不断的变化过程中,一般人很难同时对这么的细节进行思考 ,这时候就需要抽象。
    在学习编程时,抽象能力的重要性怎么说都不为过。程序员经常需要面对、处理异常复杂的业务和逻辑,如果不具备强大的抽象能力,无法把具体变成概念,进而驾驭概念进行思考,那就很难降低问题的复杂度,从而陷入泥潭,无法自拔。无论你学会了多么强大的程序语言,你的编程能力也很难有质的提高。我们只有从纷繁复杂的现象中抽取事物的本质,从具体事物提炼出正交的概念,才能驾驭这些概念,才能在一个低复杂度的世界中进行思考。
    可以说抽象能力的高低,很大程度上反映了一个程序员的能力的高低。
  2. 阅读代码的能力
    不管是C语言、python还是Java,我在起步阶段都是尝试着跟着教材或别人代码动手敲一遍,然后尝试着调通,再进行一些修改,弄清楚这段代码的含义、功能。在我这样操作是上手较快的一种方法,但它需要一定阅读代码的能力。娄老师也曾和我们强调过,对于优秀程序员来说,能够快速看懂代码的能力是必不可缺的,这样我们能够学习吸收他人良好的代码风格的思维习惯,也能够在这个编程语言层出不穷的时代快速适应新出现的编程语言。
    当然,要想能够拥有比较好的阅读代码的能力,我觉得首先你还是至少对一门较底层的语言(像C语言,虽然是高级语言却常常和底层打交道)的语法比较熟悉,这样去往其他语言迁移时也会比较方便。
  3. 具有一定的英语基础
    学程序有要有英语基础,现在大部分编程语言是基于英语语言的,比如常见的C、C++、Python等等都是英语单词堆起来的,如果学过英语在学习编程知识时可以见文知意,见闻知意什么意思呢,就是看到编程语言的某个单词就知道它在这个编程语言中的意思与作用,就容易学习编程语言了。所以最好有英语基础,这样学习编程轻松、高效一些。当然不要求英语水平有多高,英语四级水平更好,这样大部分编程语言的单词就不难理解,如果英语水平差也没关系,在学习编程时候查单词意思就行了,不过这样效率不高。
  4. 具有较好的数学基础
    另外需要数学思维能力,编程需要很强的逻辑思维能力。编程就是编写一个程序,程序是我们处理一件“事情”的流程的程序化表示,流程体现逻辑,或者可以说算法体现逻。而逻辑思考和算法与数学息息相关,比如从事算法方面工作的最好把微积分、线性代数、概率论与数理统计、离散数学等等学好。数学好逻辑比较强,算法的最高境界是数学。
  5. 编程入门必备的知识和能力
    查了很多相关的资料,再对照自己刚开始学编程语言的经历,发现下面的帖子是个很好的例子,可以参考。
    学习编程的五个关键点
    上述这些能力在sh编程中也都有比较好的体现。

三、教材内容归纳整理

思维导图

知识归纳

  1. 基本概念
    本章讨论了sh编程,阐述了sh脚本和不同版本的sh;比较了sh脚本与C程序,并指出了解释语言和编译语言的区别;详细说明了如何编写sh脚本,包括sh变量、sh语句、sh内置命令、常规系统命令和命令替换;解释了sh控制语句,其中包括测试条件、for循环、while循环、do-until 循环、case语句等;说明了如何编写sh函数以及使用参数调用sh函数。
  • sh脚本和C语言的关系
    存在相似之处,但本质不同。
    首先,sh是一个解释程序,逐行读取sh脚本文件并直接执行这些行。如果行是可执行命令且为内置命令,那么sh可直接执行。否则,它会复刻一个子进程来执行命令,并等待子进程终止后再继续,这与它执行单个命令行完全一样。
    相反,C程序必须先编译链接到一个二进制可执行文件,然后通过主sh的子进程运行二进制可执行文件。其次,在C程序中,每个变量必须有一个类型,例如char、int、float、派生类型(如struct结构体类型)等。相反,在sh脚本中,每个变量都是字符串。因此不需要类型,因为只有一种类型,即字符串。最后,每个C程序必须有一个main()函数,每个函数必须定义一个返回值类型和参数(如有)。相反,sh脚本不需要main函数。在sh脚本中,第一个可执行语句是程序的入口点。

  • 命令行参数
    在sh脚本中,可以通过位置参数$0、$1、$2等访问命令行参数。前10个命令行参数可以作为$0~$9被访问。其他参数必须称为${10}~${n},其中n>10。或者,可以通过稍后显示的shift命令查看它们。通常,$0是程序名本身,$1到$n是程序的参数。在sh中,可用内置变量$#和$*计数并显示命令行参数。
    S#=命令行参数$1到$n的数量
    $*=所有命令行参数,包括$0
    此外,sh还有与命令执行相关的以下内置变量。
    $S =执行sh的进程PID
    $?=最后一个命令执行的退出状态(如果成功,则为0,否则为非0)
    在sh中,特殊字符$表示替换,注意需要添加转义符‘’。

  #! /bin/bash
  echo $# = $#
  echo $* = $*
  echo $1 $9 $10
  echo $1 $9 ${10}
  shift
  echo $1 $9 ${10}
  ~                   

在OpenEuler虚拟机运行结果如下

  • sh变量
    shell编程中有两种变量,一种是我们自己定义的变量(自定义变量),可以通过set命令查看系统变量;第二种是Linux已定义的环境变量(如 PATH,HOME,TERM等等……这类变量我们可以直接使用) 需要记住的是:定义变量不用[$]符号,使用变量加符号即可。
    在自定义变量时,如果变量出现空格或者引号,那么也必须加引号,否则就可以省略。格外需要注意的是,自定义变量的时候,“=”左右不能有空格

  • sh中的引号
    sh有许多特殊字符,如S、/、*、>、<等。要想把它们用作普通字符,可使用或单引号来引用它们。通常,‘’用于引用单个字符。单引号用于引用长字符串。单引号内没有替换。双引号用于保留双引号字符串中的空格,但在双引号内会发生替换。

  • sh语句
    sh语句包括所有Unix/Linux命令,以及可能的I/O重定向。此外,sh编程语言还支持控制sh程序执行的测试条件、循环、case等语句。

  1. sh命令
  • 内置命令
    sh有许多内置命令,这些命令由sh执行,不需要创建一个新进程。
    下面列出一些常用的内置sh命令。
file:读取并执行文件。
break [n]:从最近的第n个嵌套循环中退出。 cd [dirname]:更换目录。
continue [n]:重启最近的第n个嵌套循环。
eval [arg ...]:计算一次参数并让sh执行生成的命令。
exec[arg ...]:通过这个sh执行命令,sh将会退出。 exit [n]:使sh退出,退出状态为n。
export [var ...]:将变量导出到随后执行的命令。 read [var ...]:从stdin中读取一行并为变量赋值。set [arg ...]:在执行环境中设置变量。
shift:将位置参数S2 S3 ...重命名为$1 S2 ...。 trap [arg][n]:接收到信号n后执行参数。umask [ddd]:将掩码设置为八进制数ddd 的。
wait [pid]:等待进程pid,如果没有给出pid,则等待所有活动子进程。

另外还有read命令:
当sh执行read命令时,它会等待来自stdin的输人行。它将输入行划分为几个标记,分配给列出的变量。read的一个常见用法是允许用户与正在执行的sh进行交互,如下面的示例所示。

echo -n "enter yes or no : "# wait for user input line from stdinread ANS
#sh reads a line from stdin
echo $ANS    # display the input string
  • Linux命令
    sh可以执行所有的Linux命令。其中,有些命令几乎已经成为sh不可分割的一部分,因为它们广泛用于sh脚本中。
    • echo命令
      echo只是将参数字符串作为行回显到stdout。它通常将相邻的多个空格压缩为一个空格,除非有引号。

      发现有报错显示,再用vim打开发现

      文件末尾莫名其妙多了一个‘`’,处理办法可以参考实践过程中[问题1](#### 时间过程)

      正常运行
    • expr命令
      格式
expr string1 OP string2 #OP = any binary operator on numbers

所有的sh变量均为字符串,我们不能直接对他们进行数值上的操作,可以通过expr语句间接更改sh变量的值。

I=123
I=I+1
echo $I  #注意这个地方在变量I前要加$号,类似PHP语言中的变量标识

运行结果

发现并不是我们想要的结果124,而是以字符串形式输出了‘I+1’
使用expr语句,首先将两个参数字符串转换位数字,然后对数字执行(二进制)操作OP,再将得到的数字转换回字符串。

I=123
I=$(expr $I + 1)
echo $I 

第一次运行报错,发现是没有严格按照expr语句格式。

修改后结果正确,输出124.

  • 管道命令
    在sh脚本中经常使用管道作为过滤器
ps -ax | grep httpd
cat file | grep word
  • 其他实用命令
awk:数据处理程序。
cmp:比较两个文件。
comm:选择两个排序文件共有的行。grep:匹配一系列文件的模式。
diff:找出两个文件的差异。
join:通过使用相同的键来连接记录以比较两个文件。 sed:流或行编辑命令。
sort:排序或合并文件。
tail:打印某个文件的最后n行。
tr:一对一字符翻译。
uniq:从文件中删除连续重复行。
  1. 命令替换
    在sh中,$A会被替换成A值。同样,当sh遇到'cmd'(用引号括起来)或$(emd)时,它会先执行cmd,然后用执行的结果字符串替换$(cmd)。

  2. sh控制语句
    常见的有if-else-fi语句、for语句、while语句、case语句等,它们的用法和逻辑都类型C语言中的使用类似。真正应用时注意格式即可,教材上有这几类语言详细的格式和使用方法,在此不做赘述。

  3. I/O重定向
    当进入sh命令时,我们可以指示sh将I/O重定向到除默认stdin、stdout和 sterr 以外的文件。I/O重定向有以下形式和含义:

>file              stdout转向文件,如果文件不存在,将会创建文件。
>>file             stdout追加到文件。
<file              将文件用作stdin;文件必须存在并具有r权限。
<<word             从“here”文件中获取输入,直到只包含“word”的行。
  1. 嵌入文档
    可以指示输出命令从stdin获取输入,将其回显到stdout,直到遇到预先安排的关键字。这些文档通常被称为嵌入文档,通常用在sh脚本中,以生成长块的描述性文本,不需要分别回显每一行。

  2. sh函数
    sh函数的定义为:

func()
{
  # function code
}

由于sh逐行执行命令,所以必须在任何可执行语句之前定义sh脚本中的所有函数。与C语言不同,在sh脚本中无法声明函数原型。sh函数的调用方式与sh脚本文件的执行方式完全相同。sh语句
fune s1 s2 ... sn
调用sh函数,以参数(字符串)形式传递s1~sn。在被调函数中,参数被引用为$0、$1到$n。通常,$0是函数名,$1到 $n是与命令行参数对应的位置参数。函数执行结束时,$?表示其退出状态,如果成功,状态为0,否则,状态为非0。$?值可用函数的显式返回值进行更改。但是,为了测试S$?的最后一次执行,必须将其分配给一个变量,然后测试该变量。

  1. sh中的通配符
  • 星号通配符

    • file *:列出当前目录中所有文件的信息
    • ls *.c:列出当前目录中所有以.c结尾的文件
  • 问号通配符

    • file ??:有3个字符的所有文件名
    • ls *.??: .后有2个字符的所有文件名
  • []通配符

    • file [ab]:包含字符a或b的所有文件名
    • ls [xyz]:列出所有包含x、y或z的文件名
    • ls [a-m]:列出包含a到m范围内字符的所有文件名
  1. 命令分组
    在sh脚本中,可以用{}或()对命令进行分组。
    {ls;mkdir abc;ls;}:通过当前sh执行中的命令列表。命令分组的唯一用处是在相同环境下执行这些命令,例如,为分组中的所有命令重定向L/O。
    更有用的命令分组是(),由subsh(进程)执行。
    (cd newdir;ls;A=value;mkdir $A):通过subsh进程执行()中的命令。subsh进程可在不影响父sh的情况下更改其工作目录。此外,当subsh进程终止时,subsh中的任何赋值变量都不起作用。

  2. eval语句
    eval会对后面的cmdLine进行两遍扫描,如果第一遍扫描后,cmdLine是个普通命令,则执行此命令;如果cmdLine中含有变量的间接引用,则保证间接引用的语义。

  • eval也可以用于回显简单变量,不一定是复杂变量。
var="TangQiheng"
echo $var
eval echo $var

eval $var 等价于echo $var

  • eval进行二次扫描

eval $var或eval 'echo $var'或eval $(echo $var)
上述命令等价,可以知道第一次扫描进行了变量替换,第二次扫描执行了该字符串中所包含的命令.

  1. 调试sh脚本
    sh脚本可由带有-x选项的子sh运行,以进行调试,如:
    bash -x mysh
    子sh将在执行命令之前显示要执行的每个sh命令,包括变量和命令替换。它允许用户跟踪命令执行。如果出现错误,sh将在错误行上停止并显示错误消息。

三、实践过程

  • 问题1 编写好脚本后,命令行执行显示找不到命令

解决方案:在编写好一个sh脚本后,注意运行脚本一定要写成 ./filename.sh,而不是 filename.sh,运行其它二进制的程序也一样,直接写 filename.sh,linux 系统会去 PATH 里寻找有没有叫 filename.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在PATH里,你的当前目录通常不在PATH里,所以写成filename.sh 是会找不到命令的,要用 ./filename.sh 告诉系统说,就在当前目录找。

  • 问题2:运行脚本时出现错误

解决方案:在出现如上报错时,往往是echo后的内容没有正确地用双引号括起来,或者多出了引号。只要增添引号让其正确配对即可。另外注意到,sh中‘#’后的内容不会在运行时打印出来,可以理解成注释。 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,每一行加个#符号太费力了,这时可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

原文地址:https://www.cnblogs.com/20191218tangqiheng/p/15324543.html