〇、简介

Shell的作用是解释执行用户的命令,用户输入一条命令,shell就行一条,这种方式成为交互式,还有另外一种方式,就是用户事先写一个shell脚本,包含很多命令,然后让shell一次性的进行执行,这种方式被称为“批处理方式”

一般我们在UNIX中使用的shell就是bash和sh,当然也有其他shell,在UNIX环境下可以使用/etc/shells:validloginshells命令来显示所有的shell,想要切换,直接输入shell名即可


一、Shell的启动

bash启动脚本是bash启动时会自动执行的脚本,因此用户可以把一些环境变量的设置和alias、umask设置等放到启动脚本中,这样每次启动shell时都会自动生效

但是,启动bash的方法不同,执行启动脚本的步骤也不同


1、作为交互登录Shell启动,或者使用--login参数启动

交互Shell指的是用户在提示符下输入命令的Shell,而不是执行脚本的shell

这样启动bash会自动执行以下脚本:

  1. 执行/etc/profil,系统中的每个用户登录时都执行,只有管理员可以修改

  2. 然后依次执行当前用户主目录的~/.bash_profile、~/.bash_login和~/.profile三个文件(如果存在的话)

  3. 在Shell退出时,会执行~/.bash_logout脚本(如果存在的话)


通常在~/.bash_profile中会有下面几行:

这样,如果~/.bashrc文件存在,还会调用这个脚本


2、以交互非登录Shell启动

比如在图形界面下开一个终端窗口,或者在登录Shell提示符下再输入bash命令,就得到一个交互非登录的shell

这种shell在启动时自动执行~/.bashrc脚本

如上面所讲,如果要在启动脚本中做某些设置,使它在图形终端窗口和字符终端的Shell中都起作用,最好就是在~/.bashrc中设置。


如果终端或远程登录,那么登录Shell是该用户的所有其他进程的父进程,所以环境变量在登录Shell的启动脚本里设置一次就可以自动带到其他非登录Shell里,而本地变量、函数、alias等设置没有办法带到子Shell里,需要每次启动非登录Shell时设置一遍,所以就需要有非登录Shell的启动脚本,所以一般来说在~/.bash_profile里设置环境变量,在~/.bashrc里设置本地变量、函数、alias等。

如果你的Linux带有图形系统则不能这样设置,由于从图形界面的窗口管理器登录并不会产生登录Shell,所以环境变量也应该在~/.bashrc里设置。


3、非交互式启动

为了执行脚本而fork出来的子Shell是非交互式Shell,启动时执行的脚本文件有环境变量BASH_ENV定义,相当于执行下面的命令:

if[-n"$BASH_ENV"];then."$BASH_ENV";fi


4、以sh命令启动

如果以sh命令启动bash,bash将模拟sh的行为

如果作为交互登录shell启动,则会一次执行:

  1. /etc/profile

  2. ~/.profile


如果作为交互式Shell启动,相当于执行

if[-n"$BASH_ENV"];then."$BASH_ENV";fi

以#!/bin/sh开头的脚本就是这种情况,不会运行任何脚本


二、Shell如何执行命令

1、执行交互式命令

凡是使用which命令查不到程序文件所在位置的命令都是shell的内建命令,这些命令相当于Shell的进程中的一个函数,没有单独的man手册,可以使用下面的命令查看:manbash-builtins

对于非内建命令,Shell会fork并exec该命令,创建一个新的进程,但是对于内建命令则不会,如cd、alias、umask、exit、export、shift、if、eval、[、for、while等命令都是内建命令

虽然内建命令不创建新的进程,但是也会有返回值,通常也用0表示调用成功,这个返回值被称为ExitStatus(状态码),可以使用特殊变量$?读出


2、执行脚本

shell脚本中用#表示注释,相当于C语言中的//注释,但是#!却表示该脚本使用/bin/sh解释执行,并不代表注释

如下例是一个简单的shell脚本:

将上面的代码保存在.sh文件中,即为一个Shell脚本

执行脚本只需要输入命令:./script.sh,这是sh./script.sh命令的简写


执行上面的脚本的步骤为:


三、Shell的基本语法

1、变量

一般的,Shell变量由全大写字母加下划线组成,有两种类型的Shell变量

(1)环境变量

环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前的Shell进程传给fork出来的子进程,使用printenv命令可以显示当前Shell进程的环境变量


(2)本地变量

只存在于当前Shell进程的变量,用set命令可以显示当前Shell进程中定义的所有变量(包括本地变量和环境变量)和函数


  • set

显示当前所有变量和函数


  • 变量的定义

环境变量是任何进程都有的概念,而本地变量是Shell所特有的概念,在Shell中,环境变量和本地变量的定义和用法相似,在Shell中定义或赋值一个变量可以使用下面的格式:

VARNAME=value

等号两边是不能有空格的,否则就会被解释成命令或命令行参数


  • export--将变量导出为环境变量

任何一个变量定义后都仅存在于当前的Shell进程,是本地变量,用export命令可以把本地变量导出为环境变量,定义和导出环境变量也可以一步完成

exportVARNAME=value

当然也可以分两步:

VARNAME=value

exportVARNAME


  • unset--删除变量

使用unset命令可以删除已经定义了的环境变量或本地变量

unsetVARNAME


  • echo--显示变量的值

使用echo命令可以显示变量的值


一般对于VARNAME变量,我们使用${VARNAME}表示他的值,在不引起歧义的情况下,我们也可以直接使用VARNAME表示他的值


Shell中的所有变量都是字符串,Shell中的变量也不需要先定义后使用,使用一个没有定义的变量,这个变量的值为空字符串


2、通配符--*、?、[]

Shell中也有通配符,如下表:

如我们可以使用lsch0[012].doc命令查找文件,如果当前目录下有ch00.doc和ch02.doc,ls的参数会直接转换成这两个文件名,而不是一个匹配字符串


3、命令代换--`或$()

由反引号(键盘上ESC键下面的,主键盘区左上角·/~)所引起来的也是一条命令,Shell会首先执行反引号中的命令,然后将结果代换到原来的位置进行原命令的执行,如下面的命令:

DATE=`date`

echo$DATE

反引号和$()是一样的:

DATE=$(date)

echo$DATE


4、算术代换--$(())

Shell会将$(())中的Shell变量的取值转换成整数用于算术计算(其他情况下Shell都将变量视为字符串,无法进行算术计算)

VAR=45

echo$(($VAR+3))

会显示48

$(())中只能进行+、-、*、/和()运算,并且只能进行整数运算


5、转义字符--\

和C语言一样,Shell中也需要转义字符,如\、\$、\\、\`、\"


6、字符串--'、"

在Shell中单引号中的所有字符都被认为是普通的字符,所以不需要转义字符,如运行:

$echo'$SHELL'

会显示$SHELL

$echo'ABC\\'

会显示ABC\\


双引号也将其中的字符串视为字面值,但是反引号、$、转义字符等等都保持原来的意义

如:

$echo"$SHELL"

会显示/bin/bash


$echo"`date`"

会显示SunApr2011:22:06CEST2003


五、Shell脚本语法

1、条件测试--test、[]

命令test或[]可以测试一个条件是否城里,如果测试结果为真,则该命令的ExitStatus为0,如果测试结果为假,则命令的ExitStatus为1(与C语言中正好相反)

由于[]中的[实际上是一个命令,他后面的都是这个命令的参数,因此需要用空格隔开

如下例:


常见测试命令如下所示:


与C语言类似,测试条件之间还可以做与、或、非逻辑运算


如:


需要注意的是,如果上例中的VAR变量没有被预先定义,那么就会被解释器展开为空字符串,整个命令就变成了:

[-dDesktop-a='abc']

这就成了一个语法错误

为了避免这样的意外情况发生,一个好的Shell编程习惯总是把变量取值放到双引号之中:


这样,虽然VAR没有被预先定义,但是命令还是被展开成了

[-dDesktop-a""='abc']


2、分支控制--if、then、elif、else、fi

和C语言类似,在Shell中使用if、then、elif、else、fi几个命令实现分支控制,例如:

if[-f~/.bashrc];then

.~/.bashrc

fi


  • :

:是一个特殊的指令,称为“空命令”,该命令不做任何事,但是ExitStatus总是真,也可以使用/bin/true或/bin/false获得总是真或假的ExitStatus

if:;thenecho"alwaystrue";fi

与下面的例子是一样的:

if/bin/true;thenecho"alwaystrue";fi


  • read

我们也可以使用read命令等待用户键入一行字符串,存到一个Shell变量中


  • &&、||

与C语言类似,Shell也提供&&与||

test"$VAR"-gt1-a"$VAR"-lt3

等价于

test"$VAR"-gt1&&test"$VAR"-lt3


因为&&操作的短路求值特性,很多Shell脚本喜欢写成:

test"$(whoami)"!='root'&&(echoyouareusinganonprivilegedaccount;exit1)


3、case、esac

case命令类似于C语言的switch/case语句,esac用来标志case语句块的结束

Shell中的case语句不仅可以用来匹配数字,也可以用来匹配字符串和通配符


如下例,每个匹配分支都可以有若干条命令,末尾必须以;;结束


下面的例子常常用在某服务的处理上,比如对于apache,我们可以输入apachestart这个命令启动他,也可以使用apachestop这个命令终止他


$0、$1...等等指的是取命令的第0个参数、第1个参数。。。


4、for、do、done

Shell脚本的for循环结构和C语言很不一样,他类似于某些编程语言的foreach循环

如下面的例子:


例子中,FRUIT是一个变量,让这个变量依次取值为apple、banana、pear做循环,done用来标志循环结束


如果目录下有chap0、chap1、chap2等文件,下面的循环将他们重命名为chap0~、chap1~、chap2~等

$forFILENAMEinchap?;domv$FILENAME$FILENAME~;done


5、while、do、done

while的用法和C语言非常类似,比如下面是一个验证密码的脚本:


我们也可以像C语言中那样控制while循环的循环次数


6、一些特殊的变量

有很多变量是被Shell自动赋值的,如下表


参数$n被称为“位置参数”

  • shift

shift命令可以令位置参数左移,比如shift3表示让$4变成$1,$5变成$2,原来的$1、$2就会被丢弃掉,而$0不移动

不带参数的shift命令相当于shift1


7、函数

Shell中的函数定义中没有返回值也没有参数列表

如下面例子所示:


注意函数体的左花括号{和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号}写在同一行,命令末尾必须有;号。

Shell脚本中的函数必须先定义后调用,一般把函数定义都写在脚本的前面,把函数调用和其它命令写在脚本的最后(类似C语言中的main函数,这才是整个脚本实际开始执行命令的地方)。


Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2等变量来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2等变量。函数中可以用return命令返回,如果return后面跟一个数字则表示函数的ExitStatus。


六、Shell脚本的调试方法

Shell提供了一些用于调试脚本的选项:

  • -n

读一遍脚本中的命令但是不执行,用于检查脚本中的语法错误

  • -v

一边执行脚本,一边将执行过的脚本命令打印到标准错误输出

  • -x

提供跟踪执行信息,将执行的每一条命令和结果依次打印出来


有三种方法使用这些选项:

  1. 在命令行提供参数$sh-x./script.sh

  2. 在脚本开头提供参数#!/bin/sh-x

  3. 在脚本中用set命令启用或禁用参数



下面是我写的一个脚本,在打开终端的时候给出人性化的提示,并且询问是否将虚拟机中的重要文件保存在宿主机上:

1

更多相关文章

  1. Linux系统中使用fdisk命令为U盘分区,并将其中一个分区设置为启动
  2. linux下lftp命令中mirror的用法详解
  3. 近 100 个 Linux 常用命令大全
  4. linux常用的命令
  5. 压缩解压常用命令
  6. 存储过程放在sql脚本里,命令行导入总是提示错误
  7. MySQL5.5.22版本安装配置以及基本命令的使用和管理数据库备份与
  8. “已有打开的与此命令相关联的 DataReader,必须首先将它关闭 ”错
  9. SQLSERVER 中表变量与临时表

随机推荐

  1. android关机充电流程及关机充电时的画面
  2. android布局---android:layout_weight
  3. Android概述--期末考试Android理论筛查
  4. [实例教程] 用python开发android应用
  5. android鼠标滚轮bug
  6. 开机动画(闪动的ANDROID字样的动画图片)
  7. Android——OOM以及内存优化
  8. 分享一些实用的Android资源
  9. 【译】Android中的安全数据— Android中
  10. android车载终端开发--------can 总线移