-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Shell 是一个用C语言编写的程序,它是用户使用Linux的桥梁。Shell既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson的sh是第一种Unix Shell,Windows Explorer是一个典型的图形界面Shell。
Shell脚本
Shell 脚本(shell script),是一种为shell编写的脚本程序。
业界所说的shell通常都是指shell脚本,但读者朋友要知道,shell和shell script是两个不同的概念。
习惯上"shell编程"都是指shell脚本编程,不是指开发shell自身。
Shell 编程跟java、php编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
第一个shell脚本
#!/bin/bash
echo "Hello world!"
#!告诉系统其后路径所指定的程序即是解释此脚本文件的Shell程序- echo命令用于向窗口输出文本
- 作为可执行程序运行:
chmod +x ./test.sh./test.sh - 作为解释器参数:
/bin/sh test.sh
Shell变量
变量名不需要如PHP那样加$美元符号,但是变量名和等号之间不能有空格,变量名需要遵循如下规则:
- 首个字符必须是字母:大小写均可
- 不能有空格,可以有下划线
- 不能使用标点符号
- 不能使用bash里的关键字:可以使用help命令查看保留关键字
使用变量
使用一个定义过的变量,只需要在变量名前加上美元$符号即可,如:
name="user1"
echo $name
name="user2"
echo ${name}
变量名外的花括号是可选的,加不加都可以;加上可以帮助解释器识别变量的边界,如:
for skill in Ada Coffe Action Java; do
echo "I'm good at ${skill}Script"
done
只读变量
使用readyonly命令可以将变量定义为只读
删除变量
变量被unset删除后不能再次使用,不能删除只读变量。
#!/base/sh
name="user1"
unset name
echo ${name}
变量作用域
- 局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量
- 环境变量:所有程序包括shell启动程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行,必要时shell脚本也可以定义环境变量
- shell变量:由shell程序设置的特殊变量,有一部分是环境变量,一部分是局部变量,这些变量保证shell的正常运行
Shell数据类型
字符串
字符串是shell编程中最常见最有用的数据类型,除了数字和字符串也没啥其他类型可用。字符串可以使用单引号,也可以使用双引号,也可以不用引号,单双引号的区别和PHP类似。
- 单引号:任何字符原样输出,中间的变量无效;中间不能出现单引号,使用转义也不行
- 双引号:双引号中可以有变量,双引号可以出现转义字符
- 字符串长度:
${#name} - 提取子串:
${name:1:4} - 查找子串:反引号
数组
bash支持一维数组,不支持多维,并且没有限定数组的大小。与C语言类似,数组的元素下标从0开始,获取数组元素要利用下标,下标可以使用整数或算术表达式,其值应大于或等于0。
- 定义数组:使用括号表示数组,数据元素用空格符号分隔开
names=(user1 user2 user3 user4)name[0]="user1"name[1]="user2"- 可以不使用连续下标,而且下标的范围没有限制
- 读取元素:${数组名[下标]}
- 使用@符合可以获取数组中的所有元素
- 数组长度:获取数组长度的方法与字符串相同
- ${#数组名[@]}
- ${#数组名[*]}
- 取数组单个元素长度:${#数组名[下标]}
Shell注释
以#开头的行就是注释,会被解释器忽略。没有多行注释,只能每行加一个#号。
每行加个井号费力,可以把这段注释代码用一对花括号括起来,定义成一个函数,没有地方调用这个代码就不会执行,达到和注释一样的效果。
Shell传递参数
我们可以在执行Shell脚本时向脚本传递参数,脚本内获取参数的格式为:$n,n为一个数字,代表执行脚本的第几个参数,从1开始。
- $#:传递到脚本的参数个数
- $*:以一个单字符串显示所有向脚本传递的参数,用双引号括起来时类似于以"$1 $2 ... $n"的形式输出所有参数
- $$:脚本运行当前进程ID号
- $!:后台运行的最后一个进程的ID号
-
$@:与$ *相同,但使用时加引号,并在引号中返回每个参数;用双引号括起来时类似于以"$1" "$2" ... "$n"的形式输出所有参数 - $-:显示shell使用的当前选项,与set命令功能相同
- $?:显示最后命令的退出状态,0表示没有错误,其他表明有错误
#!/bin/bash
for i in "$*"; do
echo $i
done
for i in "$@"; do
echo $i
done
执行和输入如下:
chmod +x test.sh
./test.sh 1 2 3
# 输出
1 2 3
1
2
3
Shell运算符
- 算术运算符
- 关系运算符
- 布尔运算符
- 字符串运算符
- 文件测试运算符
算术运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如awk和expr,expr最常用。
expr是表达式计算工具,可以完成表达式的求值操作。
#!/bin/bash
var=`expr 2 + 2`
echo "两数之和为:$var"
-
+:加法
-
-:减法
-
*:乘法
-
/:除法
-
%:取余
-
=:赋值
-
==:相等,比较两个数字,相同返回true
-
!=:不相等
-
表达式和运算符之间要有空格
-
完整的表达式要被``反括号包含,在esc键下方
-
条件表达式要放在方括号之间,并且要有空格
-
乘号前必须加反斜杠才能实现乘法运算
关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
- -eq:检测两个数是否相等
- -ne:检测两个数是否不相等
- -gt:检测左边的数是否大于右边的
- -lt:检测左边的数是否小于右边的
- -ge:检测左边的数是否大于等于右边的
- -le:检测左边的数是否小于等于右边的
布尔运算符
- !:非运算
- -o:或运算
- -a:与运算
逻辑运算符
- &&:逻辑AND
- ||:逻辑OR
字符串运算符
- =:检测两个字符串是否相等
- !=:检测两个字符串是否不想等
- -z:检测字符串长度是否为0
- -n:检测字符串长度是否不为0
- str:检测字符串是否不为空
文件测试运算符
- -b:检测文件是否是块设备文件
- -c:检测文件是否是字符设备文件
- -d:检测文件是否是目录
- -f:检测文件是否是普通文件,既不是目录页不是设备文件
- -g:检测文件是否设置了SGID位
- -k:检测文件是否设置了沾着位(StickyBit)
- -p:检测文件是否是具名管道
- -u:检测文件是否设置了SUID位
- -r:检测文件是否可读
- -w:检测文件是否可写
- -x:检测文件是否可执行
- -s:检测文件是否不为空(文件大小大于0)
- -e:检测文件/目录是否存在
Shell命令
echo命令
Shell的echo命令与PHP的指令类似,都是用于字符串输出。
- 显示普通字符串
- 显示转义字符
- 显示变量
- 显示换行:
echo -e "OK \n" - 显示结果重定向至文件:
echo "It is a test shell file" > filename - 原样输出字符串,不进行转义或取变量:使用单引号
- 显示命令执行结果:
echodata
printf命令
printf命令模仿C程序库里的printf程序,比echo移植性好,printf使用引用文本或空格分隔的参数,外面可以在printf中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
echo会自动添加换行符,而printf不会,需要手动加\n。
printf format-string [arguments...]
format-string可以使用%s %c %d %f格式替代符,%-10s指一个宽度为10个字符,-表示左对齐,没有表示右对齐,不足以空格填充,超过将内容全部显示出来。
- \a:警告字符
- \b:后退
- \c:不显示输出结果中任何结尾的换行字符
- \f:换页
- \n:换行
- \r:回车
- \t:水平制表符
- \v:垂直制表符
- \:一个字面上的反斜杠字符
- \ddd:表示1到3位八进制字符
- \0ddd:表示1到3位的八进制字符
test命令
检查某个条件是否成立,可以进行数值、字符、文件三个方面的测试。
数值测试
- -eq:等于则为真
- -ne:不等
- -gt:大于
- -ge:大于等于
- -lt:小于
- -le:小于等于
字符串测试
- =:等于
- !=:不相等
- -z:字符串长度为0
- -n:字符串长度不为0
文件测试
- -e:文件存在
- -r:文件存在且可读
- -w:
- -x:
- -s:文件存在且至少有一个字符
- -d:文件存在且为目录
- -f:文件存在且为普通文件
- -c:文件存在且为字符型特殊文件
- -b:文件存在且为块特殊文件
let命令
用于执行一个或多个表达式,变量计算中不需要加上$来表示变量
int=1
let "int++"
``
## Shell流程控制
shell的流程控制不可为空,每一个写好的流程均需执行语句。
* if...then...fi
* if...then...else...fi
* if...then...elif...then...else...fi
* for...in...do...done
* while...do...done
### 无限循环:
* while: do...done
* while true do ... done
* for (( ; ; )
### until循环
until循环执行一系列命令直至条件为真时停止,一般while循环优于until循环。
* until condition do ... done
* 条件测试发生在循环末尾,循环至少执行一次
### case语句
case语句为多选择语句,case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
case 值 in
模式1) command1
command2
;;
模式2)
command1
command2
;;
esac
case取值后面必须为单词in,每一模式必须以右括号结束,取值可以为变量或常数,匹配发现取值符合模式后,执行命令直至双分号结束。
`*`作为模式代表默认捕获,类似c++中switch的default分支。
### 跳出循环
* break命令:允许跳出所有循环,终止后面的所有循环
* continue:不跳出所有循环,仅跳出当前循环
## Shell函数
[ function ] functionname [ () ]
{
action;
[return int;]
}
* 可以带function定义,也可以不用function
* 参数返回,可以显式增加return;如果不加将以最后一条命令运行结果作为返回值,return后跟数值n(0~255)
* 调用函数可以传递参数,在函数内部通过$n的形式获取参数值,n从1开始
* 参数处理类似于Shell的命令行参数处理
## 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之间的内容作为输入
* command < infile > outfile:同时替换输入和输出,执行命令从infile读取内容,然后将输出写入outfile中
> 文件描述符0通常是标准输入stdin,1是标准输出stdout,2是标准错误输出stderr
### Here Document
Here Document是shell中的一种特殊的重定向方式,用来将输入重定向到一个交互式shell脚本或程序。
command << delimiter
document
delimiter
作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
* 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进
* 开始的delimiter前后的空格会被忽略掉
### /dev/null文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到/dev/null。
command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
如果希望屏蔽 stdout 和 stderr,可以这样写:
command > /dev/null 2>&1
> 0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)
## Shell文件包含
和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
. filename # 注意点号(.)和文件名中间有一空格
或
source filename
被包含的文件不需要可执行权限。