mkdir sh
cd sh
#! /bin/bash
cd /tmp
echo "hello world"
如何运行shell程序
- 赋予程序文件可执行权限,直接运行./shellfile:./first.sh
- 调用命令解释器(Shell)解释执行:bash first.sh
- 使用source命令执行:source first.sh
Shell的命令种类
- 内部命令:是Shell解释器本身包含的命令,在文件系统没有相应的可执行文件,如cd,echo,source,pwd。执行内部命令时,不需要创建新进程,当然也就不需要销毁进程
- source命令也称为 .命令,可以用 “.”代替source
- shell函数:用shell语言编写的一系列程序代码,可以像其他命令一样被引用
- 外部命令:会创建一个新进程来执行如:locate,grep,ifconfig,ls,find,
- 执行过程:
- 系统调用fork()创建一个Shell的复制进程(子进程)
- 在子进程的运行环境中,查找并且载入外部命令,以外部命令的程序代码取代Shell子进程,父Shell进程休眠并等待子进程执行完毕
- 子进程执行完毕后,父Shell进程被唤醒并继续从终端读取下一条命令
- 执行过程:
Shell执行命令的顺序
别名-->关键词-->函数-->内部命令-->外部命令
- type:可以查看所要执行的命令是哪种类型,type ls
- 以"#"开头的文本表示注释,会忽略#之后的所有内容
- 每一条Shell命令在推出时都会返回一个整数值给命令行Shell,该返回值表示Shell命令的退出状态,一般约定0表示成功,非零值表示失败。通过特殊变量$?查看上一条命令的退出状态,echo $?
- 各命令之间要以 ; 分隔开来;此外也可以用&&和||连接两条命令,这两种有短路特性
- 符合命令:
- 可以用 {} 或 () ,{}括起来的命令在本shell内执行,不产生新进程,()在一个子进程内执行
Shell变量
- 变量的名称应该由字母,数字或者下划线组成,并且只能以字母或下划线开头,区分大小写,名称长度没有限制
- 变量名=变量值,中间不能有空格,如果变量值中包含空格,需要加上单引号或者双引号。引用变量时,在变量名前面加上"$"符号
- shell变量本质上是一个键值对(也就是说有hash),这和python的字符串很像,也有hash
- 在变量名后面紧跟一个由非空白字符开始的字符串时,为了使变量名和气候的字符串区分开来,应该用花括号"{}"将变量名括起来
- unset命令可以将一个变量的值清除(变成空串)
- {$#变量名}:得到变量的长度,也就是字符数
- 命令替换:
- 反引号
- $(...)
- 变量属性声明:declare或者typeset
- -r:只读
- -i:整数,因为变量默认是字符串类型,所以有的时候需要这个
- -a:数组
- -f:函数
- -x:导出变量
- 数组变量:和普通变量一样,不需要先定义或者先赋值再使用,没有赋值的情况下,数组元素的值就是空串
- 赋值:数组名[下标]=值
- 引用数组元素:${数组名[下标]}
- 组合赋值:数组名=(值1,值2,值3...),各个值之间要以空格分开,比如arr=(how are you ?)
- ${arr}其实就等于${arr[0]},
- 两个特殊的变量引用:${数组名[*]}和${数组名[@]}
- ${数组名[*]}:引用数组的所有非空元素,且拼接成一个字符串
- ${数组名[@]}:引用数组的所有非空元素,仍然是单个元素,可迭代
- ${#数组名[*]}和${#数组名[@]}:数组中所有非空元素的个数
- 变量引用操作符:${...}除了简单的引用变量之外,还有字符串替换,模式匹配替换
- 字符串匹配:
- ${varname:-word}:如果varname存在且非空串,返回varname的值,否则返回word
- ${varname:=word}:如果varname存在且非空串,返回varname的值,否则将varname的值设置为word
- ${varname:+word}:如果varname存在且非空串,返回word的值,否则返回空串
- ${varname:?message}:如果varname存在且非空串,返回varname的值,否则输出message,并且退出当前脚本程序--->类似于捕获错误
- 冒号都可以省略,省略的话则只判断varname是否存在
- 模式匹配:filepath=/home/alice/major.minor.ext
- ${varname#pattern}:如果pattern匹配varname的头部,则删除最短匹配部分,返回剩余部分,varname本身不变
- echo ${filepath#/*/}---->alice/major.minor.ext
- ${varname##pattern}:如果pattern匹配varname的头部,则删除最长匹配部分,返回剩余部分,varname本身不变
- echo ${filepath##/*/}---->major.minor.ext
- ${varname%pattern}:如果pattern匹配varname的尾部,则删除最短匹配部分,返回剩余部分,varname本身不变
- echo ${filepath%.*}---->/home/alice/major.minor
- ${varname%%pattern}:如果pattern匹配varname的尾部,则删除最长匹配部分,返回剩余部分,varname本身不变
- echo ${filepath%%.*}---->/home/alice/major
- ${varname/pattern/string}或${varname//pattern/string}:如果pattern匹配varname的某个子串,则将varname的最长匹配部分替换成string,并返回替换后的串,varname本身不变。如果模式以'#'开头,则表示匹配varname的首部,如果模式以'%'开头,则表示匹配varname的尾部。如果string为空串,匹配部分将被删除。如果varname为@或者*,操作将被依次用于每个位置参数,并且扩展为结果列表。第一种格式仅替换第一次匹配的子串,第二种格式会替换所有匹配的子串
- ${varname#pattern}:如果pattern匹配varname的头部,则删除最短匹配部分,返回剩余部分,varname本身不变
- 字符串匹配:
- 位置参数和特殊变量
- 位置参数:也称为位置变量,运行Shell脚本程序时,命令行Shell传递给脚本的参数,以及在Shell脚本程序中调用函数时传递给函数的参数。变量的名称是以0,1,2,3......这些整数命名的。使用$0,$1,$2引用这些变量。注意:${10}这样的不能用$10表示。$0表示脚本名
- 特殊变量:
- $#:命令行上参数的个数,不包含脚本名本身,就是说$0
- $*:以一个单字符串显式向脚本程序传递所有参数,不包括$0
- $@:从参数1开始,显式向脚本程序传递的所有参数,如果放在双引号中扩展,则"$@"与"$1" "$2" "$3"...等效
- $?:上一条命令执行后的返回值,也就是退出状态,0为成功
- $$:运行脚本的当前进程进程号
- $!:上一个后台命令对应的进程号
- $.:由当前Shell设置的执行标志名组成的字符串
- 不加双引号的$@和$*的作用是一样的,加上双引号的"$@","$*"在特殊场合作用会有所不同,"$@"是引用所有参数,可以进行迭代,"$*"表示引用所有参数连接在一起,中间用空格分开,是一个字符串。"${数组名[@]}"表示引用数组整体的各个元素
- 位置变量不能由用户直接设置,但是可以用set命令间接的设置除$0之外的位置变量的值
-
#! /bin/bash # setposvar.sh set learn linux program echo $0 $1 $2 $3
- shell内置了shift,用于左移位置参数,也就是原来的$2的值赋给$1(原$1的值永远丢失),$3的值赋给$2。。。
-
#! /bin/bash # shiftposvar.sh set learn linux program echo "parameter number: $#,they are:" echo $1 $2 $3 shift echo "after shifted, parameter number: $#, they are:" echo $1 $2 $3 # execute bash shiftposvar.sh one two three # result parameter number: 3,they are: one two three after shifted, parameter number: 2, they are: two three
- read命令:用于从键盘上读取数据并且赋值给指定的变量,read命令可编写交互式脚本程序,格式:read 变量1 [2]
- export语句:
- 在同一Shell中,变量的值是可见的,但是在子shell中,父shell的变量是不可见的
- 子进程只能继承父进程的公有区和转出区中的数据,私有区是进程私有的不能继承
- 为父进程定义的变量对其子进程的运行环境不产生任何影响,为了使其各个子进程能继承父进程中定义的变量,就必须用export命令将这些变量送入进程转出区。
- 子进程可以读取但无法改变父进程中export出来的变量的值