linux 脚本 执行程序,Linux Shell之脚本的执行_是呵呵的博客-程序员宅基地

技术标签: linux 脚本 执行程序  

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

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

[email protected]:~/scripts$which sh

/bin/sh

[email protected]:~/scripts$file /bin/sh

/bin/sh: symbolic link to `dash`

[email protected]:~/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

[email protected]:~/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命令,凡是找不到程序文件所在的都是内建命令:

[email protected]:~/scripts$which cd

[email protected]:~/scripts$which which

/usr/bin/which

[email protected]:~/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 开始执行,而这个文本文件被当作命令行参数传给解释器。

来个栗子:

[email protected]:~/tmp$ls

onlyme tesh.sh

[email protected]:~/tmp$cat tesh.sh

#!/bin/rm -f

$0

[email protected]:~/tmp$./tesh.sh

[email protected]:~/tmp$ls

onlyme

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

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

解释器没有可执行权限

[email protected]:~/tmp$ls -l onlyme

-rw-rw-r-- 1 m m 6 6月 23 01:22 onlyme

[email protected]:~/tmp$cat test.sh

#!/home/m/tmp/onlyme

ps -ef | grep $$ | grep -v grep | grep -v ps

[email protected]:~/tmp$./test.sh

bash: ./test.sh: /home/m/tmp/onlyme: 解释器错误: 权限不够

结果报错了。

解释器有可执行权限,但是内容不靠谱

[email protected]:~/tmp$ls onlyme -l

-rwxrwxr-x 1 m m 6 6月 23 01:22 onlyme

[email protected]:~/tmp$cat onlyme

hello

[email protected]:~/tmp$cat test.sh

#!/home/m/tmp/onlyme

ps -ef | grep $$ | grep -v grep | grep -v ps

[email protected]:~/tmp$./test.sh

m 31054 5944 0 02:59 pts/14 00:00:00 bash

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

[email protected]:~/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了。

解释器不存在

[email protected]:/home/m/tmp# ls

onlyme test.sh

[email protected]:/home/m/tmp# cat test.sh

#!/home/m/tmp/onlyme2

echo $SHELL

[email protected]:/home/m/tmp# ./test.sh

bash: ./test.sh: /home/m/tmp/onlyme2: 解释器错误: 没有那个文件或目录

没有shebang这一行

[email protected]:~/tmp$cat test.sh

ps -ef | grep $$ | grep -v grep | grep -v ps

[email protected]:~/tmp$./test.sh

m 30534 5944 0 02:14 pts/14 00:00:00 bash

[email protected]:~/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接管了。

执行脚本时指定了解释器

[email protected]:~/tmp$cat test.sh

\#!/bin/bash

ps -ef | grep $$ | grep -v grep | grep -v ps

[email protected]:~/tmp$dash test.sh

m 30610 5944 0 02:24 pts/14 00:00:00 dash test.sh

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

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

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

[email protected]:~/tmp$ls

onlyme test.sh

[email protected]:~/tmp$cd ..;ls

blog new program test.sh tmp wine-git 公共的 视频 文档 音乐

examples.desktop patches scripts timerecords VirtualBox VMs workspaces 模板 图片 下载 桌面

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

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

[email protected]:~/tmp$(cd ..;ls)

blog new program test.sh tmp wine-git 公共的 视频 文档 音乐

examples.desktop patches scripts timerecords VirtualBox VMs workspaces 模板 图片 下载 桌面

[email protected]:~/tmp$ls

onlyme test.sh

果然达到了脚本的效果~ 但是这种办法有个缺点:长度有限。shell能接受的单行命令的长度是有限的,不过一般都是够用的。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42511091/article/details/116681150

智能推荐

java动态规划算法_南风知易✓✓✓的博客-程序员宅基地

java 动态规划算法递归算法的时间复杂度=递归的次数递归函数本身的时间复杂度*

典型电路的收集与记录__Jason^_^的博客-程序员宅基地

5V to 3V3通过AMS1117-3.3,将电压降至3V3,钽电容是必须的。蜂鸣器电路通过ss8050的NPN三极管实现对蜂鸣器的控制LED电路通过限流电阻对LED的电流和亮度进行控制。但每种颜色所配合的电阻还需实验,达到LED能亮同时又不晃眼睛的效果。...

Spinner的两种调用数据放式_faith_boys的博客-程序员宅基地

ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mLmtdReptStr);adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);mSpinner.setAd

ROS arbotix踩坑:‘rospkg‘、except UnicodeDecodeError, exc、dynamic module does not define init function_#君君#的博客-程序员宅基地_ros 安装rospkg

在执行roslaunch mbot_description arbotix_mbot_with_camera_xacro.launch文件时,即包含arbotix模块的程序时,报错No module named ‘rospkg’ 。报错No module named 'rospkg'经过对大家文章的查看,发现基本集中在把环境配置为python3.或者安装rospkg。但是仍没有解决。查看opt/ros/melodic/lib/python2.7/dist-packages,发现根本没有ro.

面试题:实现数组扁平化_weixin_34056162的博客-程序员宅基地

什么是数组扁平化数组扁平化是指将一个多维数组变为一维数组reduce 方法实现reduce 本身就是一个迭代循环器,通常用于累加,所以根据这一特点有以下:const arr1 = [1,[4,6],[8,3,[19,38]]]function flatten(arr) { return arr.reduce((result, item)=> { return re...

JfreeChart用法总结_shiyan0634的博客-程序员宅基地

一、简介 WW 的发展使得基于因特网的应用程序不再局限于静态或者简单的动态内容提供。传统的一些以软件包形式发布应用程序例如报表系统等都在逐渐搬到因特网上。但是这两者之间有着天壤之别,虽然对于数据获取、业务处理等方面基本类似,但是最大的差别在于用户界面。为了能在web浏览器上显示要求用户界面使用 HTML以及图片的方式来展现数据,而传统的一些利用操作系统本身的控件来开发的用户界面无法适应琳琅满目

随便推点

nginx x-sendfile下载_anjile1234567890的博客-程序员宅基地

    // 利用nginx直接下载文件,提高效率    public function download_apk_efficient($internal_file_path,$file_name){        // And redirect user to internal location        header("Content-Type: application/vn...

聊聊身份认证的那些事(开篇)_罗斯839的博客-程序员宅基地_cas流程

开篇身份认证,对于一个安全的应用来说,是第一道门槛,它为后续所有的安全措施提供了“身份”这样一个关键信息。通常每个公司会有好几个外部应用,如果每一个应用使用独立的账号体系,管理起来会非常复杂,用户也需要保存好几个账号密码,严重影响使用体验。而且公司内部的各个管理或开发系统,比如各种服务器、confluence、jira、gitlab等也都使用单独的账号,一个开发人员要记住各种纷繁杂乱的账号和密码,如果不提前保存下来,根本不可能能靠记忆完成输入。这时,如何设计一个简单易用的身份认证体系就显得尤为重要

Vscode的使用小技巧_weixin_30399821的博客-程序员宅基地

命令行启动code如果你的系统是Linux系统(我使用的是Ubuntu 16.04)这样就可以直接使用 code + filename来编辑文件(就像vi + filename)如果你的系统是MacOS 就需要在vscode里面按 command + shift + p 之后输入 shell 基本上在第一提示里面就会显示安装code,如图所示转载于:https://www.cnblo...

SpringMVC -> 转发(forward)和重定向(redirect)_欧皇小德子的博客-程序员宅基地_springmvc转发的关键字是redirect

默认写法就是转发:return的字符串将对应的前端页面转发到要求的url上配置了视图解析器的viewsresolver @RequestMapping("/hello/{a}/{b}") public String hello(@PathVariable int a, @PathVariable int b, Model model) { model.addAttribute("haha", "结果:" + (a + b)); return "hello"; }没有视图解析器

ros kinetic 自带opencv3 与 opencv 2 的兼容问题_原野寻踪的博客-程序员宅基地

问题分析自己写的系统必须基于opencv 2.x,而ros kinetic自带了opencv3的版本。于是在编译时报错:/usr/bin/ld: CMakeFiles/xxxx.cpp.o: undefined reference to symbol '_ZN2cv6String10deallocateEv'/opt/ros/kinetic/lib/x86_64-linux-gnu/l...

Latex002 | 详细教程:LaTeX 编译器哪个好?——如何在 Visual Studio Code 中全流程编写 LaTeX(上篇)_爱学习的Allan的博客-程序员宅基地_latex编译器

你是否在编写 LaTeX 过程中遇到了编译器“不给力”,无法自动补全、缩进等问题?本文比较了流行的 LaTeX 的编译器,并简要分析了其优势与不足,最终给出解决方案。