shell是一个用c语言编写的应用程序,是用来和linux内核打交道的。比如我们在终端输入ls,那么shell就会帮我们把命令翻译成内核可以识别的指令,从而访问内核所提供的服务。
shell脚本指的是shell可以识别的脚本程序。shell和shell脚本之间的关系就类似于python解释器和py文件之间关系一样,一个是脚本解释器,一个是脚本。只不过为了方便,我们把shell就当成shell脚本了,但实际上shell和shell脚本是两码事。
Shell 编程跟 java、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
Linux 的 Shell 种类众多,我们只需要知道两个即可:
Bourne Shell,也就是我们一般会在shell脚本的第一行写上#!/bin/sh
Bourne Again Shell,也就是我们一般会在shell脚本的第一行写上#!/bin/bash
这两种方式一般区分的不是特别明显,因此些#!/bin/sh或者#!/bin/bash都行。
但是真要说区别的话,这两者之间有什么区别呢?
对于#!/bin/sh来说,如果中间的代码出现了错误,那么后面的代码不会执行。
但如果是#!/bin/bash的话,中间代码出现了错误,后面的代码仍然会执行
为什么会出现这样的错误呢?
因为sh一般设成bash的软链,在一般的linux系统中,使用#!/bin/sh相当于#!/bin/bash --posix
所以,sh跟bash的区别,实际上就是bash有没有开启posix模式的区别
所以,可以预想的是,如果第一行写成 #!/bin/bash --posix,那么脚本执行效果跟#!/bin/sh是一样的
我们来写一个shell脚本吧,当然啦,必须是hello world
#!/bin/sh
echo hello world
echo "hello world"
# 解释一下,第一行表示我们指定的shell解释器,注意如果没有这一行,会使用默认的解释器
# 但是一般情况下,我们会指定为#!/bin/sh 或者#!/bin/bash
# 第二行和第三行表示输入hello world,区别就是没有加上双引号,这里加与不加均可,但是一般我们都加上。
# 这里的#表示注释,echo类似于python中的print。
shell脚本以.sh结尾,直接执行即可。但是可能会报错,显示没有权限,这时候通过chmod 755 xx.sh赋予权限即可执行。
变量的定义规则和平常的编程语言是一样的,使用字母数字下划线,并且开头不能是数字。
但是需要注意的一点是:等号之间不能有空格,比如在python中,定义一个变量可以是name = "satori",并且pep8建议等号之间要空格,看起来更工整,但是shell脚本里面这么定义是不可以的,等号之间不可以有空格,因此要这么定义name="satori"
#!/bin/sh
name="mashiro"
age=16
# 如何打印变量呢?通过$变量名 的方式
echo "name is $name,age is ${age}"
# 但是注意到我们在打印age的时候加上了{}
# 其实加不加都无所谓的,但是我们最好还是加上
# 为什么呢?比如说$nameaaa,这里我想打印name在连接上aaa,但如果这样的话会把nameaaa当成一个整体
# 而这个变量显然没有被定义,因此如果我们加上{}就不会出现这个问题了。${name}aaa,就会输出mashiroaaa
# 这里name后面是逗号,所以没事
对于一些整型、字符型的变量,就不用说了。我们还可以嵌入一些命令`
#!/bin/sh
echo "下面我要创建文件了"
# 创建一个文件,shell会逐行执行,执行到这里会创建一个文件。
# 在哪里创建呢?会在当前shell脚本所在的目录里面创建
# 在终端里面执行的命令是可以直接放到shell里面来的
touch "fuck_shanshan.txt"
# 给一个变量赋值,我们也可以将命令执行的结果赋值给一个变量
# 注意到pwd会打印当前的工作区,所以我们这里是``,表示执行原生的命令,将命令的结果赋值给work_dir
# 如果是"pwd",那么表示将pwd这三个字符赋值给work_dir
work_dir=`pwd`
echo "我当前的工作区是:${work_dir}"
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
#!/bin/bash
v="哈哈哈"
readonly v # 设置为只读变量
echo "${v}" # 可以访问
v="咯咯咯" # 尝试修改
使用 unset 命令可以删除变量,直接unset 变量名 即可,但是unset不能删除只读变量
运行shell时,会同时存在三种变量:
字符串是shell编程中最常用的数据类型(话说除了数字和字符串,也没啥其他类型好用了),字符串可以使用单引号、也可以使用双引号,也可以不使用引号,一般我们使用双引号即可。
拼接字符串
#!/bin/bash
name="古明地觉"
where="东方地灵殿"
# 多个字符串直接拼接即可之间
# "a""b""c" --> abc
echo "${name}""来自于""${where}"
使用#即可
#!/bin/bash
name="古明地觉"
echo "${#name}"
也可以看出,这里是用字符来计算的,不是用字节来计算的。
#!/bin/sh
string="helloworld"
echo "${string:1}"
echo "${string:1:4}"
变量后面加上:,即可提取子字符串,string:1:4表示从索引为1的位置开始取,取4个,注意shell里面所以也是从0开始的。如果是string:1的话,那么会从索引为1的位置取到结尾
#!/bin/sh
string="helloworld"
echo "这个会输出啥嘞,${string:100}" # 这里表示从索引为100的位置取到结尾
echo "${string:1:100}" # 从索引为1的位置开始,取100字符
字符串的截取(补充)
#号截取,删除左边字符,保留右边字符。
var="http://www.bilibili.//com"
echo "${var#*//}"
# var表示的是变量,#是运算符,表示删除var中右边的字符,*//表示以第一个//结尾的任意字符
# 所以结果是www.bilibili.//com
##号截取,删除左边字符,保留右边字符
var="http://www.bilibili.//com"
echo "${var##*//}"
# 和#的区别就是,##类似于正则中的贪婪模式,如果是##,那么*//表示最后一个以//结尾的任意字符
# 所以结果是com
%号截取,删除右边字符,保留左边字符
var="http://www.bilibili.//com"
echo "${var%//*}"
# 和#正好相反,删除右边的字符,以//开头的任意字符
# 所以结果是http://www.bilibili.
%% 号截取,删除右边字符,保留左边字符
var="http://www.bilibili.//com"
echo "${var%%//*}"
# 不用我说了
# 结果是http:
从左边第几个字符开始,及字符的个数
var="http://www.bilibili.//com"
echo "${var:0:6}"
# http:/
从左边第几个字符开始,一直到结束。
var="http://www.bilibili.//com"
echo "${var:6}"
# /www.bilibili.//com
从右边第几个字符开始,及字符的个数
var="http://www.bilibili.//com"
echo ${var:0-7:3}
# 其中的 0-7 表示右边算起第七个字符开始,3 表示字符的个数。
# 输出结果是i./
从右边第几个字符开始,一直到结束。
var="http://www.bilibili.//com"
echo ${var:0-7}
# i.//com
开始就已经提过了
以 # 开头的行就是注释,会被解释器忽略。但这是单行注释,此外还有多行注释
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
EOF 也可以使用其他符号:
:<<'
注释内容...
注释内容...
注释内容...
'
:<<!
注释内容...
注释内容...
注释内容...
!
我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
类似于python里面的sys.argv,sys.argv[0]表示文件名,sys.argv[1]表示第一个参数。。。。依次类推
#!/bin/sh
name="$1"
age="$2"
gender="$3"
echo "name is ${name},age is ${age},gender is ${gender}"
可以看到$0 $1 $2等等都是系统的保留变量,那么除此之外,还有哪些保留的变量呢?
#!/bin/bash
echo "shell传递参数实例"
echo "一共传过来$#个参数"
echo "第一个参数为$0" # 如果我们设置了 xx="$0",那么这里要写${xx},如果直接写的话,直接$0即可,切记不可以这么写:${$0}
echo "传递到所有脚本的所有参数是:$*"
echo "传递到所有脚本的所有参数是:$@"
echo "脚本运行的当前进程的id号是:$$"
echo "后台运行的最后一个进程的id号是:$!"
echo "程序的退出状态,0是正常退出哦。状态为:$?"
那么$*和$@到底有神马区别,假设我们传递了1 2 3 4 5这五个参数,那么$*的话,等价于"1 2 3 4 5"(传递了一个参数),$@等价于传递了"1""2""3""4""5"(传递了五个参数)
我们不妨检测一下,这里使用了for循环,我们后面会介绍
#!/bin/bash
echo "--\$*演示--" # \表示转义字符
for i in "$*";do
echo "${i}"
done
echo "--\$@演示--"
for i in "$@";do
echo "${i}"
done
bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为,数组名=(值1 值2 ... 值n)
例如array_name=(value0 value1 value2 value3)
array_name=(
value0
value1
value2
value3
)
读取数组元素值的一般格式是:${数组名[下标]}
例如:value=${array_name[n]}
使用 @ 符号可以获取数组中的所有元素,例如:echo ${array_name[@]}
#!/bin/sh
arr=("mashiro" 16 "东方地灵殿")
echo "${arr[0]}今年${arr[1]}岁,来自于${arr[2]}"
echo "直接打印所有内容:${arr[@]}"
获取数组长度的方法与获取字符串长度的方法相同,例如:
#!/bin/sh
arr=(1 2 3 4 5)
# 获取数组元素的个数,这里加上了[@]
echo "${#arr[@]}" # 或者${#arr[*]}也是一样的
# 如果没有[@]
echo "${#arr}"
# 直接输出数组
echo "${arr}"
bash不支持原生的数学运算,但是我们可以通过expr来实现
#!/bin/bash
val=`expr 2 + 2` # 表示计算2+2的值,注意+两边要有空格,而且这里是反引号,expr 表示计算后面的值
echo "${val}"
a=10
b=20
val=`expr ${a} + ${b}`
echo "${val}"
# 如果+两边没有空格
val=`expr 2+2`
echo "${val}"
val=`expr ${a}+${b}`
echo "${val}"
#!/bin/bash
a=10
b=20
val=`expr $a + $b`
echo "a+b=${val}"
val=`expr $a - $b`
echo "a-b=${val}"
val=`expr $a \* $b` # 注意:如果是乘法,那么*前面要加上echo "a*b=${val}"
val=`expr $a / $b`
echo "a/b=${val}"
val=`expr $a % $b`
echo "a%b=${val}"
val=`expr $a == $b`
echo "a==b=${val}"
val=`expr $a != $b`
echo "a!=b=${val}"
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。假设a=10,b=20
这个代码示例,在流程控制中会介绍
假设a=10,b=20
假设a=10,b=20
假定变量 a 为 "abc",变量 b 为 "efg"
file="/root/sh/1.sh"
-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。
注意:凡是没有用代码举例的运算符,一般都是放在if else语句里面的,后面会说。这里想说的是,这些条件都是放在[]里面的,关键是[ ]前后要和代码有一个空格,这是必须的。也就是我们的条件是判断a和b是否相等,要这么写[ $a -eq $b ],不可以这么写,[$a -eq $b], 内容不能和[]两边挨着
很早之前就介绍过了,这里再复习一遍
#!/bin/sh
echo "my name is mashiro"
echo my name is mashiro # 这里的""可以省略
#!/bin/bash
echo "\"my name is mashiro\""
echo \"my name is mashiro\" # 同理""也可以省略
#!/bin/sh
read name # read name 表示从命令行读取,然后将值赋值给name
echo "my name is ${name}"
同理read也可以接收多个变量
#!/bin/sh
read name age gender
echo "$name $age $gender"
printf 命令模仿 C 程序库(library)里的 printf() 程序。
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n。
#!/bin/bash
echo "hello world"
printf "hello world\n"
#!/bin/sh
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
注意在shell里面,如果不需要else,那么就不要写。如果是其它编程语言的话,可以加上else不写逻辑,但是shell不行
if语法格式
if condition
then
command1
command2
...
commandN
fi
也可以写成一行,但是要加上; if condition;then command;fi
末尾的fi就是if倒过来拼写,后面还会遇到类似的。
if else 语法格式
if condition
then
command1
command2
...
commandN
else
command
fi
if else-if else 语法格式
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
#!/bin/sh
read age gender
# 注意这里可不要写成 $age >= 18,这是不行的。
# 对于是否相等,则既可以用$age == 18和$age -eq 18
# 而且字符串要用=来判断,注意是=,不是==
if [ $age -ge 18 -a $gender = "f" ]
then
echo "perhaps we can make a deal"
else
echo "不约"
fi
#!/bin/sh
read age gender
if [ $age -ge 18 -o $gender = "f" ]
then
echo "perhaps we can make a deal"
else
echo "不约"
fi
#!/bin/bash
a=10
b=10
if [ $a == $b ]
then
echo "a和b是相等的"
fi
if [ $a -eq $b ]
then
echo "a和b是相等的"
fi
#!/bin/bash
read a b
if [ $a -gt $b ]
then
echo "a是大于b的"
elif [ $a -lt $b ]
then
echo "a是小于b的"
else
echo "a是等于b的"
fi
for循环一般格式为:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
或者
for var in item1 item2 ... itemN;do
command1
command2
...
commandN
done
写成一行:for var in item1 item2 ... itemN; do command1; command2…; done;
in列表是可选的,如果不用它,for循环使用命令行的位置参数。
#!/bin/bash
for v in 1 2 3 4 5
do
echo "val=${v}"
done
arr=("a" "b" "c" "d")
for v in ${arr[@]} # 遍历数组,记得加上[@],否则只打印第一个元素
do
echo "val=${v}"
done
while循环用于不断执行一系列命令,也用于从输入文件中读取数据;命令通常为测试条件。其格式为:
while condition
do
command
done
#!/bin/sh
num=1
while [ $num -lt 5 ] # 当num小于5的时候
do
echo "num=$num"
let "num++" # let可以执行表达式,让变量计算不需要加上$
done
break结束当前循环,continue继续下一层循环
#!/bin/bash
echo "输入一个可以执行的文件"
while read file
do
if [ -x ${file} ]
then
echo "文件为${file},这是一个可以执行的文件,循环结束"
break
else
echo "文件为${file},这不是一个可以执行的文件,请重新输入"
continue
fi
done
until 循环执行一系列命令直至条件为 true 时停止。
until 循环与 while 循环在处理方式上刚好相反。
一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。
until condition
do
command
done
#!/bin/bash
a=1
until [ ! $a -le 10 ] # !表示取反
do
echo "a=${a}"
let "a++"
a=`expr ${a} + 1` # 除了let "a++"还可这么写
done
Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
#!/bin/bash
echo "请输入abcde五个字符中的一个"
read alpha
case ${alpha} in
a) echo "你选择了a"
;;
b) echo "你选择了b"
;;
c) echo "你选择了c"
;;
d) echo "你选择了d"
;;
e) echo "你选择了e"
;;
*) echo "不在abcde当中"
;;
esac
shell的case语句设计的很有个性
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
shell中函数的定义格式如下:
funcName(){
函数体
}
#!/bin/bash
mashiro(){
echo "i'm mashiro"
}
echo "调用函数啦"
mashiro # 调用函数不用加括号
echo "函数调用完毕"
#!/bin/bash
add(){
echo "输入两个数字:"
read a b
return `expr $a + $b`
}
add
num=$? # 函数的返回值,在调用完函数之和,通过$?获取。并且要马上获取,如果获取之前插入了别的命令,就获取不到了,只能获取到0。并且函数只能返回整型
echo "输入的两个数字之和为:${num}"
if [ $num -gt 10 ]
then
echo "两个数字之和大于10"
else
echo "两个数字之和小于等于10"
fi
shell中怎么给传递参数呢?还记得之前的向shell脚本传递参数的$1 $2...$n吗?
#!/bin/sh
func(){
echo "函数的第一个参数是:$1"
echo "函数的第二个参数是:$2"
echo "函数的第三个参数是:$3"
echo "函数的第四个参数是:$4"
}
func 1 2 3 4 # 直接在函数名后面传递参数即可,注意如果函数的参数超过了十个,那么从第十个开始要通过${10}这样的方式获取,要加上{}
# 函数参数的获取是从1开始的,第一个参数就是$1
和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
引用方式,通过.来引用,直接. xxx.sh文件路径 即可,注意.和shell脚本之间要有空格
比如我们写了一个fuck.sh,那么在相同目录下创建的test.sh要想引用的话,就可以通过. ./fuck.sh来引用。第一个.表示引用,第二个.则是表示引用的文件的所在目录
/root/fuck.sh
#!/bin/bash
url="http://www.bilibili.com"
/root/sh/test.sh
#!/bin/bash
. ../fuck.sh
echo "${url}"
原文:https://www.cnblogs.com/traditional/p/11260842.html