Shell脚本执行过程

一、#!有什么用

脚本程序几乎都开始于#! 符号。这个符号的作用是声明解释器。

#! 就是告诉系统本文件要由哪个解释器执行。比如写shell脚本,第一行是 #!/bin/bash或 #!/bin/sh。如果写Python脚本,第一行就是#!/usr/bin/python。

大多数脚本语言都是将#后面出现的字符当作是注释,在脚本中并不起作用。这个 #! 和这个注释的规则不冲突么?

不冲突,因为第一行的 #! 其实是内核读取的,而不是脚本解释器读取的。为什么 #! 一定要写在第一行的前两个字符,因为这是在内核里写死的,它就检查前两个字符。当内核帮你选好了脚本解释器之后,调用解释器,后续的工作就都交给解释器做了。脚本的所有内容也都会原封不动的交给解释器再次解释,包括#!。但是由于对于解释器来说,#开头的字符串都是注释,并不生效,所以解释器会忽略第一行。


二、bash如何执行shell命令?

当第一行是 #!/bin/bash的时候,实际上内核给我们启动了一个bash进程,然后把脚本内容都传递给bash执行。

(一)第一层解析

bash会以一些特殊字符作为分隔符,把文本分段解析。最主要的分隔符无疑就是回车,类似功能的分隔符还有分号";"。所以在bash脚本中是以回车或者分号作为一行命令结束标志的。

这基本上就是第一层级的解析,主要目的是将大段的命令行分段。

(二)第二层解析

这一层级主要是区分所要执行的命令。这一层级主要解析的字符是管道"|",&&、||这样的可以起到连接命令作用的特殊字符。

这一层级解析完后,就得到最基本的一条条命令了。


(三)第三层解析

这一层主要是区分出要执行的命令和其参数,主要解析的是空格和tab字符。

这一层解析完,得到一个个token或者叫单词。

(四)第四层解析

解析每个token,主要是替换

下面是整个替换流程:

1.{}替换

Brace Expansion (Bash Reference Manual)

2.~替换

Tilde Expansion (Bash Reference Manual)

3.参数和变量替换

Shell Parameter Expansion (Bash Reference Manual)

4.命令替换

Command Substitution (Bash Reference Manual)

5.算术表达式替换

Arithmetic Expansion (Bash Reference Manual)

6.重定向替换

Process Substitution (Bash Reference Manual)

7.单词分割

Word Splitting (Bash Reference Manual)

8.文件名扩展(路径替换)

Filename Expansion (Bash Reference Manual)

9.引用移除

Quote Removal (Bash Reference Manual)

在上面所有步骤完成之后,所有不是上面替换得到的,未被引用的'"\,通通都移除掉。

只有大括号扩展、单词拆分、文件名扩展可以增加单词个数。

其它扩展将单个单词扩展为单个单词。唯一的例外是"$@"和$*,以及"${name[@]}"和${name[*]}的展开。

(五)判断第一个token的类型

优先顺序是:

别名:alias

关键字或者叫保留字:keyword

函数:function

内建命令:built in

哈希索引:hash

外部命令:command

先判断token是不是别名,如果不是则下一步

判断token是不是关键字,如果不是则下一步

判断token是不是函数,如果不是则下一步

判断token是不是内建命令,如果不是则下一步

判断token是不是在hash表里,如果不是则下一步


哈希索引:hash
hash功能实际上是针对外部命令做的一个功能。外部命令放在 $PATH路径中。bash在执行一个外部命令时所需要做的操作是:如果发现这个命令是个外部命令就按照 $PATH变量中按照目录路径的顺序,在每个目录中都遍历一遍,看看有没有对应的文件名。如果有,就fork、exec、wait。
$PATH变量包含的路径可能很多,目录中的文件数量也可能会很多。于是,遍历这些目录去查询文件名的行为就可能比较耗时。于是bash提供了一种功能,就是建立一个hash表,在第一次找到一个命令的路径之后,对其命令名和对应的路径建立一个hash索引。这样下次再执行这个命令的时候,就不用去遍历所有的目录了,只要查询索引就可以更快的找到命令路径,以加快执行程序的速度。

判断token是不是外部命令,如果不是则就报告命令不存在

就是在 $PATH路径下找命令,找到之后fork、exec、wait。如果没有这个可执行文件名,就报告命令不存在。

(六)调用命令

第一个token作为命令,其余token作为参数。调用命令。

三、脚本的退出

一个bash脚本的退出一般有多种方式,比如使用exit退出或者所有脚本命令执行完之后退出。无论怎么样退出,脚本都会有个返回码,而且返回码可能不同。
任何命令执行完之后都有返回码,主要用来判断这个命令是否执行成功。在交互bash中,我们可以使用 $? 来查看上一个命令的返回码。

返回码逻辑上有两类,0为真,非零为假。就是说,返回为0表示命令执行成功,非零表示执行失败。返回码的取值范围为0-255。其中错误返回码为1-255。bash为我们提供了一个内建命令exit,通过这个命令可以人为指定退出的返回码是多少。

if、while语句的条件判断实际上就是判断命令的返回值,而不是输出。

考虑到程序退出可能性的各种可能,系统将错误返回码设计成1-255,这其中还分成两类:

(1)程序退出的返回码:1-127。这部分返回码一般用来作为给程序员自行设定错误退出用的返回码,比如:如果一个文件不存在,ls将返回2。如果要执行的命令不存在,则bash统一返回127。返回码125和126有特殊用处,一个是程序命令不存在的返回码,另一个是命令的文件在,但是不可执行的返回码。

(2)程序被信号打断的返回码:128-255。这部分系统习惯上是用来表示进程被信号打断的退出返回码的。一个进程如果被信号打断了,其退出返回码一般是128+信号编号的数字。

比如说,如果一个进程被2号信号打断的话,其返回码一般是128+2=130。如:

sleep 1000
^C
echo $?
130


在执行sleep命令的过程中,我使用Ctrl+c中断了进程的执行。此时返回值为130。

可以用内建命令kill -l查看所有信号和其对应的编号:

kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX

### 回答1: 要查看shell脚本执行过程,可以使用以下方法: 1. 在脚本中添加调试信息,例如echo语句,输出变量值等。 2. 在执行脚本时添加-v选项,例如sh -v script.sh,这样会显示每个命令的执行过程。 3. 在执行脚本时添加-x选项,例如sh -x script.sh,这样会显示每个命令的执行过程,并且会将变量展开。 4. 使用set命令设置调试选项,例如set -x,这样会在脚本中所有命令执行前都显示命令本身和参数。 以上是几种常见的查看shell脚本执行过程的方法,可以根据实际情况选择适合自己的方法。 ### 回答2: 在Linux和Unix操作系统中,Shell是一个重要的命令解释器,它能够方便地执行命令。Shell脚本是由多个Shell命令组成的集合,可以被解释执行。在编写Shell脚本时,有时候我们需要查看它的执行过程,以便更好地调试和优化代码。 下面介绍几种查看Shell脚本执行过程的方法: 1. 加入调试输出 在Shell脚本中加入调试输出可以方便我们查看执行情况。可以使用echo命令输出提示或变量值,或使用set -x命令开启调试模式,以输出详细的执行过程。如下示例: #!/bin/bash set -x echo "start" var=5 echo $var set +x echo "end" 在执行以上脚本时,会输出每一步的执行过程,如下所示: $ ./test.sh + echo start start + var=5 + echo 5 5 + set +x end 2. 使用trace模式 使用trace模式可以在执行Shell脚本时打印出每一行命令以及执行的结果。可以在执行脚本时加上“-o”和“-e”选项,分别指定输出和错误信息的文件名。如下示例: $ bash -x script.sh -o output.log -e error.log 此时执行的命令会输出到output.log文件中,错误信息会输出到error.log文件中。通过查看这两个文件,可以获得Shell脚本执行的详细信息。 3. 使用bashdb进行调试 bashdb是一个命令行调试器,可以用于调试Shell脚本。它可以在执行时停下来,允许程序员逐行地检查脚本中的变量、语句和流程。使用bashdb需要安装相应的软件包,然后在执行脚本时指定“-x”选项即可。如下示例: $ bashdb -x script.sh 以上就是查看Shell脚本执行过程的几种方法,通过这些方法,可以更好地进行Shell脚本开发和调试。 ### 回答3: shell脚本是一种非交互式命令解释器,可以通过在控制台中输入一系列shell命令,将这些命令保存在脚本文件中,并通过执行脚本文件的方式来自动化执行一系列命令。在执行shell脚本过程中,我们可以查看脚本执行的详细过程,以便确定脚本是否按照预期执行。 为了查看shell脚本执行过程,我们可以采用以下方法: 1. 添加调试选项 在执行脚本时,可以通过在命令行中添加“-x”选项,来启用shell脚本的调试模式,这将显示每个执行的命令及其输出。例如: $ bash -x script.sh 2. 输出调试信息 可以在脚本中添加代码,以输出脚本执行过程的各个阶段的状态信息和变量值。例如,在脚本中添加以下代码: set -x # 开启调试模式 echo "Start executing script..." # 输出调试信息 var=10 # 定义变量 echo "var="$var # 输出变量值 set +x # 关闭调试模式 3. 使用调试器 还可以使用专门的shell脚本调试器来查看脚本执行过程。例如,bashdb是一个bash脚本调试器,它可以通过执行以下命令进行安装: $ sudo apt-get install bashdb 安装完成后,可以使用以下命令启动脚本调试器: $ bashdb script.sh 执行完以上步骤后,通过逐步执行脚本代码行来查看脚本执行的详细过程。这将有助于调试脚本中的错误或问题。 总之,以上方法可以帮助我们查看shell脚本执行过程,以便识别和解决脚本执行中的错误和问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值