当前位置: 首页 > news >正文

BASH and SH in SHELL scripts

一、执行脚本的现象

为了测试一个小的功能,写了一个小脚本,类似的内容如下:

#!/bin/shecho "start  api  test ......"for((i=1;i<=10;i++));
do
echo "cur id :" $i;
done
echo "end."

执行一下,“./testEx.sh”,结果报一个错误“sh: 5: Syntax error: Bad for loop variable”。说实话对脚本的编写还是相对少很多的,这个简单的错误勾起了探索一下的想法 。于是把执行的方式改变了下,“bash testEx.sh”,正常运行。然后把文件内容中的“#!/bin/sh”修改为“#!/bin/bash”,即使使用“./testEx,sh”,也可以正常执行。下面就分析一下原因,脚本大佬可以滑走了。

二、原因和解决

通过上述的执行,发现其实问题的产生就在于是使用sh还bash。那么系统是如何区别这两种脚本的呢?首先执行一个命令看看当前默认的脚本执行解释器:

#echo $0
bash

再执行一下命令,看看当前支持哪些脚本的解释器:

tests$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/usr/bin/sh
/bin/dash
/usr/bin/dash

也可能通过命令来查看具体的执行解释器位置:

tests$ ls /bin/*sh -l
-rwxr-xr-x 1 root root   27016  5月 31  2023 /bin/avahi-publish
-rwxr-xr-x 1 root root 1396520  1月  7  2022 /bin/bash
-rwsr-xr-x 1 root root   44808 11月 24  2022 /bin/chsh
-rwxr-xr-x 1 root root    6963 10月 13  2023 /bin/c_rehash
-rwxr-xr-x 1 root root  125688  3月 23  2022 /bin/dash
-rwxr-xr-x 1 root root    5188  3月 25  2022 /bin/gettext.sh
-rwxr-xr-x 1 root root    2181  2月  7  2022 /bin/gvmap.sh
-rwxr-xr-x 1 root root    4373  5月 24  2023 /bin/instmodsh
-rwxr-xr-x 1 root root   39460  6月  7  2023 /bin/nvidia-bug-report.sh
-rwxr-xr-x 1 root root     900  6月  7  2023 /bin/nvidia-sleep.sh
-rwxr-xr-x 1 root root   14648  2月 16  2023 /bin/pax11publish
lrwxrwxrwx 1 root root       4  5月  5  2023 /bin/rbash -> bash
lrwxrwxrwx 1 root root      21  5月  5  2023 /bin/rsh -> /etc/alternatives/rsh
lrwxrwxrwx 1 root root       4  5月  5  2023 /bin/sh -> dash  #在本机为dash,而不是bash
-rwxr-xr-x 1 root root  846888  8月 24  2023 /bin/ssh
lrwxrwxrwx 1 root root       7  5月  5  2023 /bin/static-sh -> busybox
lrwxrwxrwx 1 root root       8  5月  5  2023 /bin/tclsh -> tclsh8.6
-rwxr-xr-x 1 root root     281  5月  8  2023 /bin/vmware-license-check.sh
-rwxr-xr-x 1 root root     288  5月  8  2023 /bin/vmware-license-enter.sh
-rwxr-xr-x 1 root root   14728  3月 25  2022 /bin/xrefresh

通过上述的命令可以发现,在系统中存在着多个脚本的解释器,默认的使用是bash。但随即又出来一个问题,不同写法和不同的执行方式,到底调用了哪种解释器?把这个弄清楚了,就可以解决问题了。仍然以最开初的例子为例,这里使用以下几种方式来编辑并执行:

1、使用bash testEx.sh执行:

start  api  test ......
cur id : 1  ......  cur id : 10  end.

2、在脚本中指定#!/bin/bash,然后执行,结果同上。
3、默认执行,有可能需要增加执行权限,执行结果:

 Syntax error: Bad for loop variable

4、脚本中不指定任何解释器,执行bash testEx.sh,正常执行并显示结果同1。
5、脚本中不指定任何解释器,默认执行,正常执行,结果同上。

上面的5种方式只有3这种情况会出异常,而在前面的命令中发现它调用的是sh->dash。明确使用bash的,正常执行,显示结果;未明确在脚本中指定的,使用系统默认的bash,所以结果均一致。
通过上述例子就可以明白 :如果使用bash执行脚本或者在脚本中指定bash脚本解释器(#!/bin/bash),则始终以bash脚本解释器来处理脚本;如果脚本 指定为“#!/bin/sh”,则这个标准的解释器会调用其真正链接的解释器,在本文中它链接的是dash。使用默认执行则看系统默认的脚本解释器是如个即可。

三、扩展

"#!"体现在文件中就是一个2字节魔数,魔数开发者应该都清楚,就是代表文件类型的特殊标记,在此处即为可执行的脚本。在“#!”之后,一般是一个脚本解释器的路径,当然这个解释器可以是一个普通认知的解释器,如bash,sh,csh,dash等等,也可以是一个程序。这就看具体的应用方式了。比如下面的例子:

#!/bin/cat
echo "Used cat!"

这个脚本使用Cat命令来处理,直接执行脚本“./testCat.sh”,执行结果为(如果使用bash testCat.sh看看结果是什么?):

#!/bin/cat
echo "Used cat!"

那么bash和sh在实际的应用中有什么不同呢?一个简单的说法是前者包括后者;或者说后者是前者的一个子集。从前面命令执行也可以看出,bash提供了对posix的支持。所以才可以将循环写成和C语言类似的方式。如果执行“/bin/bash --posix”则会发现它和“/bin/sh”执行的结果一致,这下就明白了吧,sh是bash的posix支持版。

四、总结

脚本用处真得很大,尤其在一些监控、测试的情况下,一个简单的小脚本就可以达到控制程序的目的。特别是粘连一些相关工具如Jenkins和部署时,可以极大的减少代码的工作量并迅速构建迅速部署。当然脚本的缺点也不少,最显著的就是不好调试,对一些复杂的函数和变量往往需要死记硬背。
建议大家都要好好学习一下脚本相关的知识,这样在服务端开发中,会大提高工作效率。原来总以为脚本是运维的事儿,后来才发现大谬。好好学习,犹未晚尔!

相关文章:

  • 【办公软件使用分享—Excel篇】实用技巧 一学就会
  • 【面试题】网络IP协议(第六篇)
  • 数据库自动备份到gitee上,实现数据自动化备份
  • 微软Edge浏览器全解析
  • Unity 动画事件
  • opencv颜色识别,hsv采用滑块调节
  • 连接Sql Server时报错:无法通过使用安全套接字层加密与 SQL Server 建立安全连接
  • MySQL高级-SQL优化-insert优化-批量插入-手动提交事务-主键顺序插入
  • Python只读取Excel文件的一部分数据,比如特定范围的行和列?
  • 90%铲屎官不知道养猫好物,希喂、安德迈、小米猫用空气净化器分享
  • whisper 实现语音转文字
  • C语⾔数据类型和变量
  • Python+Pytest+Allure+Yaml+Pymysql+Jenkins+GitLab接口自动化测试框架详解
  • Windows下快速安装Open3D-0.18.0(python版本)详细教程
  • 新华三通用大模型算力底座方案:为AI时代注入强大动力
  • [译]Python中的类属性与实例属性的区别
  • 【comparator, comparable】小总结
  • Android Studio:GIT提交项目到远程仓库
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • Javascript Math对象和Date对象常用方法详解
  • javascript 总结(常用工具类的封装)
  • Linux各目录及每个目录的详细介绍
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • Otto开发初探——微服务依赖管理新利器
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 闭包--闭包之tab栏切换(四)
  • 彻底搞懂浏览器Event-loop
  • 从伪并行的 Python 多线程说起
  • 浮动相关
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 目录与文件属性:编写ls
  • 使用parted解决大于2T的磁盘分区
  • 详解移动APP与web APP的区别
  • 容器镜像
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • (04)odoo视图操作
  • (C)一些题4
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (转)四层和七层负载均衡的区别
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .net mvc部分视图
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .Net面试题4
  • .NET学习全景图
  • .NET与 java通用的3DES加密解密方法
  • .NET中使用Protobuffer 实现序列化和反序列化
  • /etc/skel 目录作用
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [30期] 我的学习方法
  • [BZOJ 3680]吊打XXX(模拟退火)