AI智能
改变未来

shell的魔幻世界


(一)shell初识


最底下是计算机硬件,然后硬件被系统核心包住,在系统核心外层的就是shell,然后shell外层的就是我们最容易理解的应用程序,我们平时接触最多的就是应用程序了。

看图可知:shell其实是一个命令解释器,它的作用是解释用户输入的命令和程序,命令和程序可以理解成我们图中的应用程序,我们linux系统中的那些命令其实也都是一个个的小程序,只不过完成的是系统的应用功能。我们在系统的终端中输入一条命令,可以立马看到一条或者几条系统回复我们的信息,其实就是shell在帮我们回复,所以shell可以称之为命令解释器。这种从键盘一输入命令,就可以立马得到相应的回复信息,叫作交互的方式,相当于我们在和电脑交流。Shell存在于系统的最外层,所以算作操作系统的外壳,它之外的应用程序就不能算作操作系统了。我们从输入系统的账户密码开始,到登录系统以后的所有操作都是shell在帮我们解释执行的。
Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。

了解了shell之后,我们再来了解下shell脚本。如果我们的命令或者应用程序不在命令行直接执行,而是通过一个程序文件来执行时,这个程序就被称之为shell脚本。Shell脚本里面通常内置了多条命令,有的还包含控制语句,比如if和else的条件控制语句,for和select的循环控制语句等。这些内置在一个shell脚本中的命令通常是一次性执行完成,不会不停的返回信息给用户,这种通过文件执行脚本的方式称之为非交互方式。Shell脚本类似于windows下的批处理,但是它比批处理要强大一些,现在windows下有一个叫做power shell的功能可以和linux下的shell功能媲美。

Windows下的文本文件就是文本文件,不能被执行,但是linux下的文本文件只要给她的文档属性改成可以执行文件的,只要用chmod命令就可以了。
windows下若想使用shell需要配合git的应用程序。

(二)探索shell命令

2.1、第一个shell脚本

打开文本编辑器(可以使用 vi/vim 命令来创建文件),新建一个文件 test.sh,扩展名为 sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好,如果你用 php 写 shell 脚本,扩展名就用 php 好了。写一句代码输出 hello world:

#!/bin/bash     (bash可以改成sh)echo \"Hello World !\"#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。echo 命令用于向窗口输出文本。

运行 Shell 脚本有两种方法:
1、作为可执行程序
将上面的代码保存为 test.sh,并 cd 到相应目录:

chmod +x ./test.sh  #使脚本具有执行权限./test.sh  #执行脚本

一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样,直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,你的当前目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。
2、作为解释器参数
直接运行解释器,其参数就是 shell 脚本的文件名,如:

/bin/sh test.sh/bin/php test.php这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

2.2、shell变量

定义变量时,变量名不加美元符号($,PHP语言中变量需要),如:

your_name=\"runoob.com\"

注意,变量名和等号之间不能有空格,同时,变量名的命名须遵循如下规则:
1、命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
2、中间不能有空格,可以使用下划线(_)。
3、不能使用标点符号。
4、不能使用bash里的关键字(可用help命令查看保留关键字)。

除了显式地直接赋值,还可以用语句给变量赋值,如:

for file in `ls /etc`或者for file in $(ls /etc)以上语句将 /etc 下目录的文件名循环出来。

注:在Linux中反引号起着命令替换的作用,命令替换命令替换是指Shell可以先执行反引号中的命令,将输出结果暂时保存,在适当的地方输出.

`command`

$() 也有命令替换的作用
1、单引号、双引号用于用户把带有空格的字符串赋值给变量时的分界符。
2、如果没有单引号或双引号,shell会把空格后的字符串解释为命令。
3、单引号和双引号的区别。单引号告诉shell忽略所有特殊字符,而双引号忽略大多数,但不包括 $ 、\\ 、`。
4、 ${ } 中放的是变量,例如echo ${hello} 取hello变量的值并打印,也可以不加括号比如 $hello。

2.3、使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

your_name=\"qinjx\"echo $your_nameecho ${your_name}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java; doecho \"I am good at ${skill}Script\"done

如果不给skill变量加花括号,写成echo “I am good at $skillScript”,解释器就会把 $skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。
推荐给所有变量加上花括号,这是个好的编程习惯。

已定义的变量,可以被重新定义,如:

your_name=\"tom\"echo $your_nameyour_name=\"alibaba\"echo $your_name

这样写是合法,但注意,第二次赋值的时候不能写
$ your_name=“alibaba”,使用变量的时候才加美元($)。

获取字符串长度:

string=\"abcd\"echo ${#string} #输出 4

提取子字符串:

string=\"runoob is a great site\"echo ${string:1:4} # 输出 unoo

查找子字符串:
查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):

string=\"runoob is a great site\"echo `expr index \"$string\" io`  # 输出 4

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

使用 unset 命令可以删除变量。语法:

unset variable_name

变量被删除后不能再次使用。unset 命令不能删除只读变量。

2.4、变量类型

运行shell时,会同时存在三种变量:
1、局部变量: 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
2、环境变量 : 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3、shell变量: shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。

2.5、shell数组

在 Shell 中,用括号来表示数组,数组元素用\”空格\”符号分割开。定义数组的一般形式为:

array_name=(value0 value1 value2 value3)

还可以单独定义数组的各个分量:

array_name[0]=value0array_name[1]=value1array_name[n]=valuen

读取数组:

valuen=${array_name}

使用 @ 符号可以获取数组中的所有元素,例如:

echo ${array_name[@]}

2.6、shell注释

以 # 开头的行就是注释,会被解释器忽略。

通过每一行加一个 # 号设置多行注释。

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

多行注释:

:<<EOF注释内容...注释内容...注释内容...EOFEOF也可以换成其他符号如\'或者!等

2.7、Shell 传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推…
以下实例我们向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名:

#!/bin/bash# author:菜鸟教程# url:www.runoob.comecho \"Shell 传递参数实例!\";echo \"执行的文件名:$0\";echo \"第一个参数为:$1\";echo \"第二个参数为:$2\";echo \"第三个参数为:$3\";

为脚本设置可执行权限,并执行脚本,输出结果如下所示:

$ chmod +x test.sh$ ./test.sh 1 2 3Shell 传递参数实例!执行的文件名:./test.sh第一个参数为:1第二个参数为:2第三个参数为:3

$* 与 $@ 区别:
相同点:都是引用所有参数。
不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 \” * \” 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。

#!/bin/bash# author:菜鸟教程# url:www.runoob.comecho \"-- \\$* 演示 ---\"for i in \"$*\"; doecho $idoneecho \"-- \\$@ 演示 ---\"for i in \"$@\"; doecho $idone

执行脚本,输出结果如下所示:

$ chmod +x test.sh$ ./test.sh 1 2 3-- $* 演示 ---1 2 3-- $@ 演示 ---123

注: Shell 里面的中括号(包括单中括号与双中括号)可用于一些条件的测试:
算术比较, 比如一个变量是否为0 [$var -eq 0 ]。
文件属性测试,比如一个文件是否存在 [ -e $var ]。
是否是目录 [ -d $var ]。
两个字符串是否相同 [[ $var1 = $var2 ]]。
[] 常常可以使用 test 命令来代替。

2.8、shell运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
例如,两个数相加(注意使用的是反引号 ` 而不是单引号 \’):

#!/bin/bashval=`expr 2 + 2`echo \"两数之和为 : $val\"

表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
完整的表达式要被


包含,注意这个字符不是常用的单引号,在 Esc 键下边。

+   加法     `expr $a + $b` 结果为 30。-	减法	`expr $a - $b` 结果为 -10。*	乘法	`expr $a \\* $b` 结果为  200。/	除法	`expr $b / $a` 结果为 2。%	取余	`expr $b % $a` 结果为 0。=	赋值	a=$b 将把变量 b 的值赋给 a。==	相等。用于比较两个数字,相同则返回 true。	[ $a == $b ] 返回 false。!=	不相等。用于比较两个数字,不相同则返回 true。	[ $a != $b ] 返回 true。

条件表达式要放在方括号之间,并且要有空格
例如: [$ a==$ b] 是错误的,必须写成 [ $a == $b ]。

关系运算符:
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

-eq	检测两个数是否相等,相等返回 true。	[ $a -eq $b ] 返回 false。-ne	检测两个数是否不相等,不相等返回 true。	[ $a -ne $b ] 返回 true。-gt	检测左边的数是否大于右边的,如果是,则返回 true。	[ $a -gt $b ] 返回 false。-lt	检测左边的数是否小于右边的,如果是,则返回 true。	[ $a -lt $b ] 返回 true。-ge	检测左边的数是否大于等于右边的,如果是,则返回 true。	[ $a -ge $b ] 返回 false。-le	检测左边的数是否小于等于右边的,如果是,则返回 true。	[ $a -le $b ] 返回 true。

布尔运算符:
假定变量 a 为 10,变量 b 为 20:

!	非运算,表达式为 true 则返回 false,否则返回 true。	[ ! false ] 返回 true。-o	或运算,有一个表达式为 true 则返回 true。	[ $a -lt 20 -o $b -gt 100 ] 返回 true。-a	与运算,两个表达式都为 true 才返回 true。	[ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符:

&&	逻辑的 AND	[[ $a -lt 100 && $b -gt 100 ]] 返回 false||	逻辑的 OR	[[ $a -lt 100 || $b -gt 100 ]] 返回 true

字符串运算符:
假定变量 a 为 “abc”,变量 b 为 “efg”:

=	检测两个字符串是否相等,相等返回 true。	[ $a = $b ] 返回 false。!=	检测两个字符串是否相等,不相等返回 true。	[ $a != $b ] 返回 true。-z	检测字符串长度是否为0,为0返回 true。	[ -z $a ] 返回 false。-n	检测字符串长度是否不为 0,不为 0 返回 true。	[ -n \"$a\" ] 返回 true。$	检测字符串是否为空,不为空返回 true。	[ $a ] 返回 true。

文件测试运算符:
文件测试运算符用于检测 Unix 文件的各种属性。
属性检测描述如下:

-b file	检测文件是否是块设备文件,如果是,则返回 true。	[ -b $file ] 返回 false。-c file	检测文件是否是字符设备文件,如果是,则返回 true。	[ -c $file ] 返回 false。-d file	检测文件是否是目录,如果是,则返回 true。	[ -d $file ] 返回 false。-f file	检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。	[ -f $file ] 返回 true。-g file	检测文件是否设置了 SGID 位,如果是,则返回 true。	[ -g $file ] 返回 false。-k file	检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。	[ -k $file ] 返回 false。-p file	检测文件是否是有名管道,如果是,则返回 true。	[ -p $file ] 返回 false。-u file	检测文件是否设置了 SUID 位,如果是,则返回 true。	[ -u $file ] 返回 false。-r file	检测文件是否可读,如果是,则返回 true。	[ -r $file ] 返回 true。-w file	检测文件是否可写,如果是,则返回 true。	[ -w $file ] 返回 true。-x file	检测文件是否可执行,如果是,则返回 true。	[ -x $file ] 返回 true。-s file	检测文件是否为空(文件大小是否大于0),不为空返回 true。	[ -s $file ] 返回 true。-e file	检测文件(包括目录)是否存在,如果是,则返回 true。	[ -e $file ] 返回 true。

其他检查符:

-S: 判断某文件是否 socket。-L: 检测文件是否存在并且是一个符号链接。

2.9、shell的echo命令

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

echo string

显示普通字符串:

echo \"It is a test\"=echo It is a test

这里的双引号完全可以省略。赋值时,如果变量内容中有空格,则会造成赋值不完整。而在输出内容时,会将含有空格的字符串视为一个整体来输出;如果内容中有命令(要反引下)、变量等,则会先把变量、命令解析出结果,然后输出最终内容;如果字符串中带有空格等特殊字符,则有可能无法完整地输出,因此需要双引号替代无引号的情况,特别是对变量赋值时改加双引号。一般连续的字符串、数字、路径等可以不加任何引号进行赋值和输输出,不过最好是用用。
显示转义字符:

echo \"\\\"It is a test\\\"\"

显示换行:

echo -e \"OK! \\n\" # -e 开启转义echo \"It is a test\"

结果:

OK!It is a test

显示不换行:

#!/bin/shecho -e \"OK! \\c\" # -e 开启转义 \\c 不换行echo \"It is a test\"

结果:

OK! It is a test

显示结果定向至文件:

echo \"It is a test\" > myfile

原样输出字符串,不进行转义或取变量(用单引号):

echo \'$name\\\"\'

结果:

$name\\\"

显示命令执行结果:

echo `date`

2.10、Shell printf 命令

printf 命令模仿 C 程序库(library)里的 printf() 程序。
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \\n。

printf 命令的语法:

printf  format-string  [arguments...]#format-string: 为格式控制字符串#arguments: 为参数列表。

用一个脚本来体现printf的强大功能:

#!/bin/bash# author:菜鸟教程# url:www.runoob.comprintf \"%-10s %-8s %-4s\\n\" 姓名 性别 体重kgprintf \"%-10s %-8s %-4.2f\\n\" 郭靖 男 66.1234printf \"%-10s %-8s %-4.2f\\n\" 杨过 男 48.6543printf \"%-10s %-8s %-4.2f\\n\" 郭芙 女 47.9876

结果如下所示:

姓名     性别   体重kg郭靖     男      66.12杨过     男      48.65郭芙     女      47.99#%s %c %d %f都是格式替代符#%-10s 指一个宽度为10个字符#(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字#符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显#示出来。#%-4.2f 指格式化为小数,其中.2指保留2位小数。

2.11、shell的test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
数值测试:

-eq	等于则为真-ne	不等于则为真-gt	大于则为真-ge	大于等于则为真-lt	小于则为真-le	小于等于则为真
if test $[num1] -eq $[num2]

字符串测试:

=	等于则为真!=	不相等则为真-z 字符串	字符串的长度为零则为真-n 字符串	字符串的长度不为零则为真

文件测试:

-e 文件名	如果文件存在则为真-r 文件名	如果文件存在且可读则为真-w 文件名	如果文件存在且可写则为真-x 文件名	如果文件存在且可执行则为真-s 文件名	如果文件存在且至少有一个字符则为真-d 文件名	如果文件存在且为目录则为真-f 文件名	如果文件存在且为普通文件则为真-c 文件名	如果文件存在且为字符型特殊文件则为真-b 文件名	如果文件存在且为块特殊文件则为真
if test -e ./bash

注:
Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级分别为:\”!“最高,”-a\”次之,\”-o\”最低。

2.12、shell函数

linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:

[ function ] funname [()]{action;[return int;]}

1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255
下面的例子定义了一个函数并进行调用:

#!/bin/bash# author:菜鸟教程# url:www.runoob.comdemoFun(){echo \"这是我的第一个 shell 函数!\"}echo \"-----函数开始执行-----\"demoFunecho \"-----函数执行完毕-----\"

结果:

-----函数开始执行-----这是我的第一个 shell 函数!-----函数执行完毕-----

函数参数:
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…
带参数的函数示例:

#!/bin/bash# author:菜鸟教程# url:www.runoob.comfunWithParam(){echo \"第一个参数为 $1 !\"echo \"第二个参数为 $2 !\"echo \"第十个参数为 $10 !\"echo \"第十个参数为 ${10} !\"echo \"第十一个参数为 ${11} !\"echo \"参数总数有 $# 个!\"echo \"作为一个字符串输出所有参数 $* !\"}funWithParam 1 2 3 4 5 6 7 8 9 34 73

结果:

第一个参数为 1 !第二个参数为 2 !第十个参数为 10 !第十个参数为 34 !第十一个参数为 73 !参数总数有 11 个!作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

注:
$ 10 不能获取第十个参数,获取第十个参数需要$ {10},当n>=10时,需要使用${n}来获取参数。

2.13、Shell 输入/输出重定向

大多数 UNIX 系统命令从你的终端接受输入并将所产生的输出发送回​​到您的终端。一个命令通常从一个叫标准输入的地方读取输入,默认情况下,这恰好是你的终端。同样,一个命令通常将其输出写入到标准输出,默认情况下,这也是你的终端。
重定向命令列表如下:

command > file	将输出重定向到 file。command < file	将输入重定向到 file。command >> file	将输出以追加的方式重定向到 file。n > file	将文件描述符为 n 的文件重定向到 file。n >> file	将文件描述符为 n 的文件以追加的方式重定向到 file。n >& m	将输出文件 m 和 n 合并。n <& m	将输入文件 m 和 n 合并。<< tag	将开始标记 tag 和结束标记 tag 之间的内容作为输入。需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

通过 wc -l 命令计算 Here Document 的行数:

$ wc -l << EOF欢迎来到菜鸟教程www.runoob.comEOF3          # 输出结果为 3 行$

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到\”禁止输出\”的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:

$ command > /dev/null 2>&1

2.14、shell文件包含

和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
Shell 文件包含的语法格式如下:

. filename   # 注意点号(.)和文件名中间有一空格或source filename

实例:
创建两个 shell 脚本文件。
test1.sh 代码如下:

#!/bin/bash# author:菜鸟教程# url:www.runoob.comurl=\"http://www.runoob.com\"

test2.sh 代码如下:

#!/bin/bash# author:菜鸟教程# url:www.runoob.com#使用 . 号来引用test1.sh 文件. ./test1.sh# 或者使用以下包含文件代码# source ./test1.shecho \"菜鸟教程官网地址:$url\"

为 test2.sh 添加可执行权限并执行:

$ chmod +x test2.sh$ ./test2.sh菜鸟教程官网地址:http://www.runoob.com#被包含的文件 test1.sh 不需要可执行权限。
filepath=$(cd \"(dirname \"$0\")\";pwd) #相对路径# $0:当前运行脚本路径# direname \"$0\":当前脚本父目录# $(direname \"$0\"):取返回父目录的值# cd到父目录# 最后输出地址
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » shell的魔幻世界