Linux Shell之脚本的执行

(1)shell如何执行一个命令

Linux的命令分为两类:一类是shell的内建命令;另一类则是独立于shell的命令。别忘了,shell也只是系统中的一个程序而已,当它执行非内建命令时,本质上是在呼叫另一只程序,比如ls。下面验证一下:

m@meng:~/scripts$ which sh
/bin/sh
m@meng:~/scripts$ file /bin/sh
/bin/sh: symbolic link to `dash`
m@meng:~/scripts$ file /bin/dash
/bin/dash: ELF 32-bit LSB  shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=1fff1896e5f8db5be4db7b7ebab6ee176129b399, stripped
m@meng:~/scripts$ file /bin/ls
/bin/ls: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=25210dc46cdefbe29cf3d2b5ceef3eceb6e2a57e, stripped

这段代码说明两个问题: Ubuntu中sh默认链接的是dash;dash和ls是两个独立的ELF文件,即可执行文件,这意味着二者的地位是等同的。从源码的角度讲,它们都有自己的main函数,编译成二进制后都有自己的_start入口。所以,在shell中执行ls命令,shell需要另建一个进程,即调用fork();然后在新进程中调用exec()执行ls的代码。

但是对于内建命令,shell执行时相当于调用自己的一个函数,即调用自己的一部分代码,这显然无需新建进程。Shell应该有办法区分哪些命令是内建的,哪些不是。对人类来说,区分的办法就是使用which命令,凡是找不到程序文件所在的都是内建命令:

m@meng:~/scripts$ which cd
m@meng:~/scripts$ which which
/usr/bin/which
m@meng:~/scripts$ man cd
没有 cd 的手册页条目

cd竟然是内建命令。。。而且内建命令还没有man手册!查看内建命令的手册应使用man builtins命令。

(2)shell如何执行一个脚本

众所周知,执行一个shell脚本一般有两种办法:1、sh script.sh 2、chmod +x script.sh && ./script.sh

第一种办法不需要脚本具有可执行权限,而且可以在sh后面加各种选项进行调试,其原理就是新建一个进程,在新进程中执行sh,同时把脚本文件当做参数传给sh,由sh逐行读取脚本中的命令,然后就像上面执行命令一样。不过我也很好奇:为什么不直接由当前这个shell来执行脚本呢?干嘛还要费事新建一个?下面我们就会知道。

第二种办法需要给脚本赋予可执行权限(这很可能是两种办法的关键区别,可执行权限到底意味着什么?),然后似乎直接当ELF文件给执行了。。。就像执行./a.out一样,这是什么情况?然后我们就要引入shebang的概念~

shell脚本的第一行往往是这样开头的:

#!/bin/bash

“#!”被称为shebang,可以说这是shell脚本的标准起始行,第一行一般都这样写。它的作用是指明执行该脚本所使用的程序,要注意的是,shebang后面的程序必须使用绝对路径,而且不一定非要是sh、dash、csh等shell,也可以是任意一个可执行的文件,比如sed、rm这类命令;或者某个可执行的脚本文件

有了shebang之后,直接执行脚本的原理是这样的(摘抄自《Linux C一站式编程》):

Shell会 fork 一个子进程并调用 exec 执行 ./script.sh 这个程序, exec 系统调用应该把子进程的代码段替换成 ./script.sh 程序的代码段,并从它的 _start 开始执行。然而 script.sh 是个文本文件,根本没有代码段和 _start 函数,怎么办呢?其实 exec 还有另外一种机制,如果要执行的是一个文本文件,并且第一行用Shebang指定了解释器,则用解释器程序的代码段替换当前进程,并且从解释器的 _start 开始执行,而这个文本文件被当作命令行参数传给解释器。

来个栗子:

m@meng:~/tmp$ ls
onlyme  tesh.sh
m@meng:~/tmp$ cat tesh.sh
#!/bin/rm -f
$0
m@meng:~/tmp$ ./tesh.sh
m@meng:~/tmp$ ls
onlyme

test.sh的内容就是删除它本身,它的shebang中的执行程序就是rm -f,而$0代表脚本本身,所以脚本执行之后,它消失了。。。

那么,如果shebang中的执行程序不靠谱或者根本没有shebang,还硬要用第二种办法执行,会发生什么呢?

+ 解释器没有可执行权限

m@meng:~/tmp$ ls -l onlyme
-rw-rw-r-- 1 m m 6  6月 23 01:22 onlyme
m@meng:~/tmp$ cat test.sh
#!/home/m/tmp/onlyme
ps -ef | grep $$ | grep -v grep | grep -v ps
m@meng:~/tmp$ ./test.sh
bash: ./test.sh: /home/m/tmp/onlyme: 解释器错误: 权限不够

结果报错了。

  • 解释器有可执行权限,但是内容不靠谱
m@meng:~/tmp$ ls onlyme -l
-rwxrwxr-x 1 m m 6  6月 23 01:22 onlyme
m@meng:~/tmp$ cat onlyme
hello
m@meng:~/tmp$ cat test.sh
#!/home/m/tmp/onlyme
ps -ef | grep $$ | grep -v grep | grep -v ps
m@meng:~/tmp$ ./test.sh
m        31054  5944  0 02:59 pts/14   00:00:00 bash

预料中的报错没有出现,反而脚本可以正常执行。这是因为,解释器无法正常执行时(有执行权限),父shell会接管脚本。父shell就是输入脚本时所在的shell,我们可以验证一下:

m@meng:~/tmp$ sh
$ /home/m/tmp/test.sh
m        31093 31091  0 03:03 pts/14   00:00:00 /bin/sh /home/m/tmp/test.sh

输入sh则进入了sh世界,当前的shell不再是bash了,然后执行脚本(内容没变,还是显示当前进程使用的shell),发现做解释器的shell已经变成了/Bin/sh了。

  • 解释器不存在
root@meng:/home/m/tmp# ls
onlyme  test.sh
root@meng:/home/m/tmp# cat test.sh
#!/home/m/tmp/onlyme2
echo $SHELL
root@meng:/home/m/tmp# ./test.sh
bash: ./test.sh: /home/m/tmp/onlyme2: 解释器错误: 没有那个文件或目录
  • 没有shebang这一行
m@meng:~/tmp$ cat test.sh
ps -ef | grep $$ | grep -v grep | grep -v ps
m@meng:~/tmp$ ./test.sh
m        30534  5944  0 02:14 pts/14   00:00:00 bash
m@meng:~/tmp$ sh
$ /home/m/tmp/test.sh
m        30551 30550  0 02:17 pts/14   00:00:00 /bin/sh /home/m/tmp/test.sh

跟第二种情况的结果一样:父shell接管了。

  • 执行脚本时指定了解释器
m@meng:~/tmp$ cat test.sh
\#!/bin/bash
ps -ef | grep $$ | grep -v grep | grep -v ps
m@meng:~/tmp$ dash test.sh
m        30610  5944  0 02:24 pts/14   00:00:00 dash test.sh

显然,执行脚本时指定的解释器会覆盖shebang的内容。

(3)命令行中临时的脚本

如果不想把一些命令组合写进一个文件中再执行(略麻烦),而且这个组合没有长期保存的必要,只是临时的用一下,那么可以使用分号把一堆命令摆在一行上然后执行:

m@meng:~/tmp$ ls
onlyme  test.sh
m@meng:~/tmp$ cd ..;ls
blog              new      program  test.sh      tmp             wine-git    公共的  视频  文档  音乐
examples.desktop  patches  scripts  timerecords  VirtualBox VMs  workspaces  模板    图片  下载  桌面

这样做的好处是,如果第一个命令花费时间很长,那么使用分号将不必等待它执行完后再输入第二条命令。可以提前输入第二条命令,等第一条命令执行完后自动执行第二条。

但是,这样做还不够“脚本化”,因为这些命令会改变父shell的环境,比如cd命令。而真正的脚本是在一个新进程中执行的,执行完后返回到父shell中并不改变父shell的环境。为了达到这一点,需要将这些命令组合用圆括号括起来:

m@meng:~/tmp$ (cd ..;ls)
blog              new      program  test.sh      tmp             wine-git    公共的  视频  文档  音乐
examples.desktop  patches  scripts  timerecords  VirtualBox VMs  workspaces  模板    图片  下载  桌面
m@meng:~/tmp$ ls
onlyme  test.sh

果然达到了脚本的效果~

但是这种办法有个缺点:长度有限。shell能接受的单行命令的长度是有限的,不过一般都是够用的。

时间: 2024-10-10 18:19:01

Linux Shell之脚本的执行的相关文章

Linux shell 自启动脚本写法

# ********************************************************************** # Linux shell 自启动脚本写法 # 说明: # 我们在做系统的时候,写自启动脚本是常有的事,下面是一个样板分析. # # 2017-1-10 深圳 南山平山村 曾剑锋 # ********************************************************************** #!/bin/sh # 脚本

linux shell 创建并发后台执行任务并等待任务执行完成

#!/bin/bash echo "i am main" for i in $(seq 1 5) do { sleep 10; echo "i am $i"; }& done wait http://www.cnblogs.com/dorothychai/archive/2012/10/19/2730374.html linux shell 创建并发后台执行任务并等待任务执行完成

python文件读写操作与linux shell变量命令交互执行

python对文件的读写还是挺方便的,与linux shell的交互变量需要转换一下才能用,这比较头疼! 1 #coding=utf-8 2 #!/usr/bin/python 3 import os 4 import time 5 #python执行linux命令 6 os.system(':>./aa.py') 7 #人机交互输入 8 S = raw_input("input:") 9 os.environ['S']=str(S) 10 #把字符串S写入文件 11 outpu

Linux Shell 小脚本经典收藏

1.在两个文件中找出相同的号码 diff -y xx.txt oo.txt | egrep -v "<|>" | awk '{print $2}' 2.打印第几行到第几行之间 cat 1 | awk 'NR==2,NR==4{print}' 1.删除Linux远程用户连接会话 [[email protected] logs]# w 10:45:28 up 15 days, 16:23, 4 users, load average: 0.00, 0.00, 0.00 USER

linux shell -常用脚本

1.判断登录用户 1.1脚本 [[email protected]_1 shell]$ vi check_user.sh #! /bin/sh echo "You are logged in as `whoami`"; if [ `whoami` != devtac ]; then echo "Must be logged in as devtac to run this script." exit fi echo "Running script at `

Linux Shell 01 脚本与变量

一 脚本的创建和执行 1. 创建文件*.sh,文件后缀为sh 2. 编辑脚本 首行必须为:#!/bin/bash   #指定解释脚本的shell 3. 赋权 chmod u+x a.sh 4. 执行 ./a.sh 二 shell脚本中的变量 1.变量的定义与删除 name="hello" #定义变量=前后不能有空格 unset name #删除变量 a. 当前shell中定义的变量只在当前shell有效 b. 不论是普通变量还是环境变量,归根结底都是变量,都遵循变量的基本操作 2.环境

linux shell 学习脚本笔记

[[email protected] C07]# cat 7_1.sh #!/bin/bash if [ -f /etc/hosts ]   then     echo "[1]" fi if [[ -f /etc/hosts ]]   then     echo "[[1]]" fi if test -f /etc/hosts   then     echo "test1" fi [[email protected] C07]# cat 7_2

Linux Shell 相关记录

http://www.tutorialspoint.com/unix/unix-shell.htm Linux Shell 获取脚本的所在目录的绝对路径 basedir=$(cd $(dirname $0); pwd;) echo $basedir shell 执行错误马上退出,而不是继续执行 set -e

使用Runtime.exec()运行windwos dos或linux shell命令

使用Runtime.exec()运行windwos dos或linux shell命令,按实际情况具体测试 实例代码: package com.bookoo.test.command; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter