参考:https://www.runoob.com/linux/linux-shell.html
shell基础
1.特别强调:
脚本的执行方式两大种:
1.在.sh文件当前目录下使用:
/bin/sh test.sh 或 /bin/bash test.sh
说明:/bin/sh 和 /bin/bash 使用的解析器不同,有的语法不支持,
2.在.sh文件当前目录下使用:
chmod +x ./test.sh # 将文件变为可执行文件
./test.sh # 执行当前可执行文件
3.文件是在windows系统创建后传送到linux系统时,文件类型不正确(dos类型),
需要在vim编辑状态下修改文件类型(unix): set ff=unix
4.shell中变量作用域:默认为global(全局),在函数中需要尽量使用local(局部)指定为局部变量,
避免方法调用时,使用到同名的变量,导致数据出错。
5.以下语法在bash解析器中使用,在sh解析器中可能会报错
2.变量:
2.1定义 str="hello world" 注意:等号前后不能空格
2.2引用 echo ${str} 或 echo $str # 打印在控制台
2.3命名规范: 同java
2.4设置只读: readonly str # 设置后不能再次赋值
2.5删除变量: unset str # 删除后不能在引用
3.字符串:
3.1单引号:变量和转义符号不生效,原样展示
echo '\"${str}\" 你好' # 结果: \"${str}\" 你好
3.2双引号:可以解析变量和转义符号
echo "\"${str}\" 你好" # 结果: "hello world" 你好
3.3字符串拼接:直接将两个变量紧挨一起,中间不用任何符号
echo "hello""world" # 结果: helloworld
3.4截取子串: ${str:1:3} # 结果: ell 从索引1,取3个,起始索引为0
3.5长度: ${#str} # 结果: 11
3.6查找子串: echo `expr index "${str}" eo` # 结果: 2
说明:查询出e或o中第一个出现的位置
4.数组:
4.1定义:使用括号加空格
方式1 array=("11" "2" "35")
方式2 array=(
"11"
"2"
"35"
)
方式3 array[0]="11"
4.2引用:
${array[0]} # 结果: 11
${array[@]} 或 ${array[*]} # 数据所有元素
4.3数组长度: ${#array[@]} 或 ${#array[*]}
注意:${#array[0]} # 表示数组第一个元素的长度,结果: 2
5.参数传递:
#!/bin/bash
echo "shell 参数传递实例"
echo "执行的文件名: $0";
echo "第一个参数为: $1";
echo "第二个参数为: $2";
echo "第三个参数为: $3";
echo "=============================="
echo '参数个数$# '$#;
echo '所有参数$* '$*;
echo '脚本运行的当前进程ID号: '$$;
echo '后台运行的最后一个进程ID号: '$!;
echo '所有参数$@ '$@;
echo '所有参数"$@" '"$@";
echo '退出状态$? '$?
为脚本设置可执行权限,并执行脚本,同时传递参数,参数以空格分割
chmod +x test.sh
./test.sh 1 2 3
echo '=============$* 与 $@ 区别=================='
echo '$*返回一个值,$@返回的多个值'
echo '-- $* 演示 ---'
for i in "$*"; do
echo $i
done
echo '-- $@ 演示 ---'
for i in "$@"; do
echo $i
done
结果:
=============$* 与 $@ 区别==================
$*返回一个值,$@返回的多个值
-- $* 演示 ---
1 33 54
-- $@ 演示 ---
1
33
54
6.运算符
6.1 基本运算符
原生bash中不支持简单数学运算,通过其他命令实现,如awk和expr
加: `expr ${a} + ${b}`
减: `expr ${a} - ${b}`
乘: `expr ${a} \* ${b}`
除: `expr ${a} / ${b}`
取余: `expr ${a} % ${b}`
赋值: a=${b}
数字相等: [${a} == ${b}] 返回true或false
数字不等: [${a} != ${b}] 返回true或false
注意:在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 "*" 不需要转义符号 "\" 。
例子
#!/bin/bash
# 算术运算
a=10;
b=20;
echo "a + b = `expr ${a} + ${b}`";
echo "a - b = `expr ${a} - ${b}`";
echo "a * b = `expr ${a} \* ${b}`";
echo "a / b = `expr ${a} / ${b}`";
echo "a % b = `expr ${a} % ${b}`";
if [ ${a} == ${b} ]
then
echo "a 等于 b"
fi
if [ ${a} != ${b} ]
then
echo "a 不等于 b"
fi
6.2 关系运算符
-eq 相等 [ $a -eq $b ]
-ne 不相等 [ $a -ne $b ]
-gt 大于
-lt 小于
-ge 大于或等于
-le 小于或等于
例子,同上使用if语句
6.3 布尔运算符
! 非 [ ! false ]
-o 或 [ $a -lt 20 -o $b -gt 100 ]
-a 与 [ $a -lt 20 -a $b -gt 100 ]
6.4 逻辑运算符
&& 逻辑and [[ $a -lt 100 && $b -gt 100 ]]
|| 逻辑or [[ $a -lt 100 || $b -gt 100 ]]
注意: 必须加两个中括号 [[]]
6.5 字符串运算符
= 检验相等,相等返回true [ $a = $b ]
!= 校验不等,不等放回true [ $a != $b ]
-z 校验长度为0,为0返回true [ -z $a ]
-n 校验长度不为0,不为0返回true [ -n "$a" ]
$ 校验非空,非空返回true [ $a ]
6.6 文件测试运算符: 用于检测 Unix 文件的各种属性
常用:
-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。
-d file 是目录,是=>true [ -d $file ] 返回 false。
-f file 是普通文件(既不是目录,也不是设备文件),是=>true [ -f $file ] 返回 true。
不常用:
-b file 是块设备文件,是=>true [ -b $file ] 返回 false。
-c file 是字符设备文件,是=>true [ -c $file ] 返回 false。
-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。
7.echo命令
7.1 普通字符串
echo "it is a test"
结果: it is a test
echo it is a test
结果: it is a test
7.2 显示转义字符
echo "\"it is a test\""
结果: "it is a test"
7.3 显示变量
#!/bin/sh
read name
echo "${name} it is a test"
结果: 张三 # 输入的内容
张三 it is a test #输出
7.4 显示换行
echo -e "it is a test \n" # -e开启转义
echo "zhangsan"
结果: it is a test
zhangsan
7.5 显示不换行
echo -e "it is a test \c" #-e开启转义 \c 不换行
echo "zhangsan"
结果: it is a test zhangsan
7.6 显示结果定向到文件
echo "it is a test" > myfile
7.7 显示原样字符串 ,用单引号
echo '$name \"'
结果: $name \"
7.8 显示命令执行结果
echo `date`
结果: Thu Jul 24 10:08:46 CST 2014
8. printf命令
语法: printf format-string [arguments...]
实例1:
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
结果:
姓名 性别 体重kg
郭靖 男 66.12
说明:
%s 字符串,%d 整型,%c 一个字符,%f 小数
10:10个字符宽
-: 左对齐,没有则右对齐
\n: 换行,默认不换行
实例2:
# format-string为双引号
printf "%d %s\n" 1 "abc"
结果: 1 abc
# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"
结果: 1 abc
# 没有引号也可以输出
printf %s abcdef
结果: abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
结果: abcdef
printf "%s\n" abc def
结果:
abc
def
printf "%s %s %s\n" a b c d e f g h i j
结果:
a b c
d e f
g h i
j
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
结果: and 0
printf 的转义序列
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),
而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符
实例3:
# %s不解析字符串中的转义符号
$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>
# %b解析字符串中的转义符号
$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>
9. test命令 (就是if判断后的[]的另一种写法)
9.1 数值测试
-eq 等于则为真
-ne 不等于则为真
-gt 大于则为真
-ge 大于等于则为真
-lt 小于则为真
-le 小于等于则为真
实例:
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
注意: 代码中的 [] 执行基本的算数运算,如:result=$[num1+num2]
9.2 字符串测试
= :等于则为真
!= :不相等则为真
-z 字符串 :字符串的长度为零则为真
-n 字符串 :字符串的长度不为零则为真
实例:
str1="abcd"
str2="bcd"
if test -n ${str1}
then
echo "str1长度不为0"
else
echo "str2长度为0"
fi
if test -z ${str2}
then
echo "str2 size is zero"
else
echo "str2 size is not zero"
fi
if test ${str1} = ${str2}
then
echo "str1 equals str2"
else
echo "str1 not equal to str2"
fi
9.3 文件测试
-e 文件名 :如果文件存在则为真
-r 文件名 :如果文件存在且可读则为真
-w 文件名 :如果文件存在且可写则为真
-x 文件名 :如果文件存在且可执行则为真
-s 文件名 :如果文件存在且至少有一个字符则为真
-d 文件名 :如果文件存在且为目录则为真
-f 文件名 :如果文件存在且为普通文件则为真
-c 文件名 :如果文件存在且为字符型特殊文件则为真
-b 文件名 :如果文件存在且为块特殊文件则为真
实例:
echo "检查/root目录下是否有shell目录,结果如下:"
cd /root
if test -d ./shell
then
echo "shell文件存在,且为目录"
elif test -e ./shell
then
echo "shell文件存在,不为目录"
else
echo "shell文件不存在"
fi
注意: 还提供 -a -o ! 操作。 优先级:!最高 -a次之 -o最低
实例2
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
10. 流程控制
10.1 条件语句
10.1.1 简单if
if condition
then
command1
command2
...
commandN
fi
写成一行(适用于终端命令提示符):
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
10.1.2 if else
if condition
then
command1
else
command2
fi
10.1.3 if else-if else
if condition
then
command1
elif condition2
command2
else
command3
fi
注意: condition处可以用test命令,也可用[] ( 如:[ $a -eq $b ] )
10.2 循环
10.2.1 for循环
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
写成一行: for var in item1 item2 ... itemN; do command1; command2… done;
实例:
array=(1 2 3)
# 循环数组打印所有值
for var in ${array[@]};
do
echo ${var};
done;
10.2.2 while循环
条件为true时继续,false时停止循环
while condition
do
command
done;
实例:
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
说明: let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中
不需要加上 $ 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
自加操作:let no++
自减操作:let no--
简写形式 let no+=10,let no-=20,分别等同于 let no=no+10,let no=no-20。
实例:
# while 用于读取键盘信息
echo '按下 ctrl + D 退出'
printf "输入你的名字:"
while read input
do
echo "${input}! 是个帅哥"
done;
10.2.3 无限循环
while :
do
command
done
或
while true
do
command
done
或
for (( ; ; ))
10.2.4 until循环
until条件为true时停止,与while相反(true继续)
until condition
do
command
done
实例:
echo "输出0-9,如下:"
y=0
until [ ${y} -gt 9 ]
do
echo "${y}"
y=`expr ${y} + 1`
# let "y++"; # Bash 的let命令
done
10.2.5 case ... esac
等同于java中的switch ... case
每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,
跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
*) 放最后等同于default
case 值 in
模式1)
command1
command2
...
commandN
;; # 等同break
模式2)
command1
command2
...
commandN
;;
esac
实例:
echo '输入1 - 4 之间的数子'
printf "你输入的数字为:"
read aNum
case ${aNum} in
1) echo '你选择了1';;
2) echo '你选择了2';;
3) echo '你选择了3';;
4) echo '你选择了4';;
*) echo '你选择了1-4之外的数字';;
esac
10.2.6 break命令(跳出循环)
10.2.7 continue命令(跳过当前循环)
实例:
# 要求:输入1-5之间的数字,继续游戏,否则结束
while :
do
printf "请输入1-5之间的数字:"
read inNum
case ${inNum} in
1|2|3|4|5) printf "你输入的数字是:%s !\n" ${inNum}
;;
*) echo "你输入的数字不在1-5之间,游戏结束 ! "
break
;;
esac
done
11. 函数
11.1 函数格式:
[ function ] funname [()]
{
action;
[return int;]
}
说明: []中的内容为非必选
1.可以写作 function fun()定义,也可写作 fun()定义,不带任何参数
2.return 加返回值,不加则以最后一行命令的结果进行返回
11.2 函数参数
函数内,通过 $n 的形式来获取参数的值,n>= 10时,需要用${10}方式获取
$# 传递到脚本或函数的参数个数
$* 以一个单字符串显示所有向脚本传递的参数
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
实例:
# 带有返回值的函数
fun3() {
# 对两个数进行相加
#return $(($1+$2))
return `expr $1 + $2`
}
echo -e "请输入数字1:\c"
read a
echo -e "请输入数字2:\c"
read b
fun3 ${a} ${b}
echo "两数的和为:$?"
注意:
1.$?不能哥着其他命令获取,只能跟着函数后,
不然获取的是第二个$?上一条命令的函数成功与否的数字含义
2.函数的定义必须放在调用之前,不然解析不到
实例:
# 求数组的所有值的和
sum(){
# x=(1 2 3 4 5 6 7 8 9 10)
x=$1
sum=0
for var in ${x[@]}
do
sum=`expr ${sum} + ${var}`
done
echo ${sum}
return ${sum}
}
arr=(1 2 3 4 5 6 7 8 9 10)
sum "${arr[*]}"
echo "数组的和为:$?"
注意:
数组参数的传递,必须写成字符串形式 "${arr[*]}"
经过测试:
如果函数参数传递数组: 如 sum ${arr} 等同于 sum 1 2 3 4 5 6 7 8 9 10 ,一共传递了10个参数
在函数中使用 echo $2 ;echo $3 分别获取到了2和3的值,得已验证了以上结论
故使用字符串形式将数组的所有值放在字符串中,如 "${arr[*]}"
如下不写在函数中,则不用字符串
arr=(1 2 3 4)
for v in ${arr[*]}
do
echo ${v}
done
12. 输入/输出重定向
命令 说明
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 之间的内容作为输入。
12.1输出重定向
command1 > file1 将command1的输出结果存入file1
实例:
who > file1 执行who命令,将用户信息存入file1,如果file1中有内容,则将覆盖
who >> file1 将内容追加到file1尾部
12.2输入重定向
command1 < file1 将file1的内容写入标准输入(STDIN)中,再提供command1使用
实例:
wc -l file1 :-l(为英文L的小写) 打印文本的行数 结果为 2 file1
wc -l < file1 :结果为 2 ,没有文件名,因为命令仅知道标准输入读取的内容
wc -l < file1 >> file1 :不打印结果,将 wc -l < file1 的结果 "2" 追加到file1文件中
12.3Here Document
特殊重定向,将输入的文本段,重定向到标准输入中
command << delimiter
document
delimiter
实例:
在命令行中用 wc -l命令计算 Here Document的行数
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
结果:
3 # 输出结果为 3 行
实例:
#!/bin/bash
cat << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
结果:
欢迎来到
菜鸟教程
www.runoob.com
12.4 /dev/null 文件
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
command > /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么
也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
$ command > /dev/null 2>&1
注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
13. 文件包含
13.1 文件引入方式
. filename # 注意点号和文件名中间有一个空格
或
source filename
13.2 被引用的文件不需要有可执行权限
实例:
1)创建test1.sh文件,文件内容如下:
#!/bin/bash
name="张三"
2)创建test2.sh文件,文件内容如下:
#!/bin/bash
# 引入test1.sh文件
. ./test1.sh
# source ./test1.sh
# 使用test1.sh文件中的变量
echo "${name}是男的"
结果:
张三是男的