zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Shell脚本学习指南(一)——入门

shell入门 脚本 学习指南
2023-09-11 14:18:26 时间


脚本编程语言与编译型语言的差异

许多中型、大型的程序都是用编译型语言写成,例如Fortran、Ada、Pascal、C、C++或Java。这类语言只要从源代码转换成目标代码,便能直接通过计算机来执行。

编译型语言的好处是高效,缺点则是:它们多半运作于底层,所处理的是字节、整数、浮点数或是其他机器层级的对象。例如,在C++里,就很难进行“将一个目录里所有的文件复制到另一个目录中”之类的简单操作。

脚本语言通常是解释型的。这类程序的执行,是由解释器读入程序代码,并将其转换为内部的形式,再执行。请注意,解释器本身是一般的编译型程序。

为什么要使用Shell脚本

使用脚本编程语言的好处是,他们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象。缺点是:他们的效率通常不如编译型语言。不过权衡之下,通常使用脚本编程还是值得的:花一个小时写成的交单脚本,同样功能用C或C++来编写,可能需要两天,而且一般来说,脚本执行的速度已经够快了,快到足以让人忽略它性能上的问题。脚本编程语言的例子有awk、Perl、Python、RuBy与Shell
因为Shell似乎是各UNIX系统之间通用的功能,并且经过了POSIX的标准化。因此,Shell脚本只要“用心写”一次,即可应用在很多系统上。因此,之所以要使用Shell脚本是基于:
简单性
Shell是一个高级语言;通过它,你可以简洁地表达复杂的操作。
可移植性
使用POSIX所定义的功能,可以做到脚本无需修改就可在不同的系统上执行。
开发容易
可以在短时间内完成一个功能强大又好用的脚本。

一个简单的脚本

让我们从一个简单的脚本开始,假设你想知道,现在系统上有多少人登录。who命令可以告诉你现在系统上有谁登录:
在这里插入图片描述这里我开启了两个ssh终端,所以提示有两个相同的用户,但是是不同的ssh用户

在大型的、多用户的系统上,所列出的列表可能很长,在你能够计算用户个数之前,列表早已滚动出屏幕画面,因此每次做这件事的时候,都会让你觉得很麻烦。这正是进行自动化的好时机。计算机用户总数的方法尚未提到。对此,我们可以利用wc(字数计算)程序,它可以算出行数、字数、与字符数。在此例中,我们用wc -l,也就是只计算行数:
who | wc -l —来统计用户个数
在这里插入图片描述|管道符号可以在两程序之间建立管道(pipeline):who的输出,成了wc的输入,wc所列出的结果就是已登录用户的个数。

下一步则是将此管道转变成一个独立的命令。方法是把这条命令输入一个一般的文件中,然后使用chmod为该文件设置执行权限,如下所示:
在这里插入图片描述这展示了小型Shell脚本的典型开发周期:首先,直接在命令行上测试,然后,一旦找到能够完成工作的适当语法,再将它们放在一个独立的脚本里,并为该脚本设置执行的权限。之后,就能直接使用该脚本。

自给自足的脚本:唯一第一行的#!

当Shell执行一个程序时,会要求UNIX内核启动一个新的进程,以便在该进程里执行所指定的程序。内核知道如何为编译型程序做这件事。我们的nusers Shell脚本并非编译型程序;当Shell内核执行它时,内核将无法做这件事,并回应“not executable format file”错误信息。Shell收到此错误信息时,就会说“啊哈,这不是编译型程序,那么一定是Shell脚本”,接着就会启动一个新的/bin/sh(标准副本)副本来执行该程序。

当文件中开头的两行字符是#!时,内核会扫描该行其余部分,看是否存在可用来执行程序的的解释器的完整路径。(中间如果出现任何空白符号都会略过)。此外,内核还会扫描是否有一个选项要传递给解释器。内核会以被指定的选项来引用解释器,再搭配命令行的其他部分。

Shell脚本通常是一开始都是#! /bin/sh。如果你的/bin/sh并不符合POSIX标准,请将这个路径改为符合POSIX标注的Shell。下面是几个初级的陷阱(gotchas),请特别留意:

  • 当今系统,对#!这一行的长度限制从63到1024都有。请尽量不要超过64个字符
  • 在默写系统上,命令行部分(也就是要传递给解释器执行的命令)包含了命令的完整路径名称。不过有些系统并不是这样;命令行的部分会原封不动的传递给程序。因此,脚本是否具有可移植性取决于是否有完整的路径名称。
  • 别在选项(option)之后放置任何空白,因为空白也会跟着选项一起传递给被引用的程序
  • 你需要知道解释器的完整路径名称。这可以用来规避可移植性的问题,因为不同的厂商可能将同样的东西放在不同的地方
  • 一些较旧的系统上,内核不具解释#!的能力,有些Shell会自行处理,这些Shell对于#!与紧随其后的解释器名称之间是否可以有空白,可能会有不同的解释。
#!/bin/bash -
who | wc -l

第一行的-表示没有Shell选项;这是基于安全上的考虑,可避免某种程度上的欺骗式攻击。

Shell的基本元素

命令与参数

Shell最基本的工作就是执行命令。以互动的方式来使用Shell很容易理解这一点:每键入一道命令,Shell就会执行。像这样:
在这里插入图片描述Shell识别三种基本命令:内建命令、Shell函数以及外部命令:

  • 内建命令是由Shell本身所执行的命令。有些命令是由于其必要性才内建的,例如cd用来改变目录,read用来将来自用户(或文件)的输入数据传给Shell变量。另一种内建命令的存在是为了效率,其中最典型的就是test命令,编写脚本时会经常用到它。另外还有I/O命令,例如echo与priintf。
  • Shell函数是功能健全的一系列程序代码,以Shell语言写成,它们可以像命令那样引用。
  • 外部命令就是由Shell的副本(新的进程)所执行的命令,基本过程如下
    a.建立一个新的进程。此进程即为Shell的一个副本。
    b.在新的进程里,在PATH变量内所列出的目录中,寻找特定的命令。当命令名称含有斜杠(/)符号时,将略过路径查找步骤。
    c.在新的进程里,以所找到的新程序取代执行中的Shell程序并执行。
    d.程序完成后,最初的Shell会接着从终端读取的下一条命令,或执行脚本里的下一条命令。
    参考下图
    在这里插入图片描述

变量

变量就是为某个信息片段所起的名字,例如first_name或driver_lic_no。所有程序语言都会有变量,Shell也不例外。
在这里插入图片描述值中包含空格时使用双引号,赋值不使用双引号,变量的连接使用双引号

简单的echo输出

在这里插入图片描述详细内容大家可以使用 man echo查看

华丽的printf输出

printf命令模仿C程序库里的printf()函数。几乎复制了该函数的所有功能。使用 man 1 printf查看细节
在这里插入图片描述

基本的I/O重定向

在你登录时,UNIX便将默认的标准输入、输出及错误输出安排成你的终端。I/O重定向就是你通过与终端交互,或是在Shell脚本里设置,重新安排从哪里输入或输出到哪里。

重定向与管道

<改变标准输入;
>改变标准输出;
>>附加到文件;
|建立管道。
tr -d '\r' < dos-file.txt | sort > UNIX-file.txt
这条命令会先删除输入文件的回车符,在完成数据排序之后,将结果输出到目的文件。
在这里插入图片描述在这里插入图片描述
细节查看 man 1 tr

特殊文件:/dev/null与/dev/tty

UNIX系统提供了两个对Shell编程特别有用的特殊文件。第一个文件/dev/null,就是大家所熟知的位桶。传送到此文件的数据都会被系统丢掉。也就是说,当程序将数据写入此文件时,会认为它以成功写入数据的操作,但实际上什么事都没做。
另一个特殊文件为/dev/tty。当程序打开此文件时,UNIX会自动将它重定向到一个终端(一个实体的控制台或串行端口,也可能是一个通过网络与窗口登录的伪终端)再与程序结合。

基本命令查找

名称为bin的目录用来保存可执行文件,bin是binary的缩写。
如果你要编写自己的脚本,最好准备自己的bin目录来存放它们,并且让Shell能够自动找到它们。这不难,只要建立自己的bin目录,并将它加入$PATH的列表即可:
在这里插入图片描述

要让修改永久生效,在.profile文件中把你的bin目录加入到$PATH,而每次登录时Shell都将读取.profile文件。
在这里插入图片描述

访问Shell脚本的参数

所谓的位置参数指的也就是Shell脚本的命令行参数。在Shell函数里,它们同时也可以是函数的参数。各参数都是由整数来命令。基于历史原因,当它超过9,就应该用大括号括起来。
在这里插入图片描述
grep命令就是用来过滤的,细节查看man 1 grep
# finduser这一行是一个注释,Shell会忽略由#开头的每一行

简单的执行跟踪

程序是人写的,难免会出错。想知道你的程序在做什么,有个好方法,就是把执行跟踪的功能打开。这会使得Shell显示每个被执行到的命令,并在前面加上"+":一个加号后面跟着一个空格。
在这里插入图片描述你也可以在脚本里,用set -x命令将执行跟踪的功能打开,然后再用set +x命令关闭它。这个功能对复杂脚本比较有用,不过这里只用简单的程序来说明:
在这里插入图片描述

国际化与本地化

编写软件给全世界的人使用,是一项艰难的挑战。整个工作通常可以分为两个部分:国际化(internationalization,缩写为i18n,因为这个单字在头尾之间包含了18个字母),以及本地化(localization,缩写为l10n,理由同前)。

了解就行,以后需要用到的时候大家再学习