运行方式
我的test.sh文件
#!/bin/bash
ls
pwd
date
第一种,为当前文件加上可执行权限,作为可执行文件,需要给代码第一行加上 #!/bin/bash
$ chmod +x test.sh
$ ./test.sh
cgdb Desktop googletest-release-1.8.0 Pictures test.sh
cgdb-0.7.0 Documents HTTP Public Videos
class Downloads Music Templates webbench-1.5
/home/jin
Fri Aug 24 13:30:07 EDT 2018
-
注意,一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当 前目录找。
第二种,作为解释器的参数,这种可以不加可执行权限,也不需要给代码第一行加上 #!/bin/bash 的shebang
$ /bin/bash test.sh
cgdb Desktop googletest-release-1.8.0 Pictures test.sh
cgdb-0.7.0 Documents HTTP Public Videos
class Downloads Music Templates webbench-1.5
/home/jin
Fri Aug 24 13:35:00 EDT 2018
-
这种运行方式是,直接运行解释器,其参数就是 shell 脚本的文件名,这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。
-
Shell脚本中用#表示注释,相当于C语言的//注释。
-
但如果#位于第一行开头,并且是则例外,它表示该脚本使用后面指定的解释器/bin/bash解释执行。
shell原理
还是以上面的 test.sh 为例,当我们在命令行敲下 ./text.sh 或者 /bin/bash test.sh 的时候其实发生了下面的事
-
Shell(父进程)首先创建子进程,接着子进程再去 exec 可执行文件,其实 exec 以后是在运行解释器
-
父进程等待子进程执行结束
-
子进程任务
-
打开文件,按行读取
-
读取第一行
-
fork孙子进程
-
孙子进程exec
-
-
孙子进程执行完成,返回,子进程读取下一行
-
fork孙子进程
-
孙子继承exec
-
-
...直到执行完当前test.sh文件中的指令
-
子进程返回
-
-
父进程等待结束,整个shell执行完成
解释内建指令的执行原理
现在将 test.sh 内容修改如下
#!/bin/bash
pwd
cd ..
pwd
我们要得到的效果是,第二次 pwd 是第一次 pwd 的上级目录
脚本执行cd命令,发现回显消息当前所处的目录发生改变,但实际上,真实目录并没有改变。这个也很好理解,毕竟要创建子进程来解释脚本。但是:
直接命令行上执行 cd 命令,发现父bash的工作目录发生了改变!这个如何理解?说好的创建子进程呢?归根结底,执行命令,不一定要创建子进程!这些不需要创建子进程的命令,叫做shell的内建命令,由父bash亲自执 行,理解上,将该类命令,理解成shell的内部函数即可。 可是
我们发现,用 . 或者 source 修饰脚本,脚本的执行影响到了父bash!source或者 . 命令是Shell的内建命令,这种方式也不会创建子Shell,而是直接在交互式Shell下逐行执行脚本中的命令。
Linux 上的内建指令
可以 man cd,因为 cd 本身就是一个内建命令