创建博客

This commit is contained in:
lemonchann
2019-11-17 01:12:14 +08:00
commit 6f0c102626
133 changed files with 20640 additions and 0 deletions

View File

@@ -0,0 +1,806 @@
---
layout: post
title: shell
tags: shell Linux script
categories: Linux
published: false
---
* TOC
{:toc}
Shell有两种执行命令的方式
* 交互式Interactive解释执行用户的命令用户输入一条命令Shell就解释执行一条。
* 批处理Batch用户事先写一个Shell脚本(Script)其中有很多条命令让Shell一次把这些命令执行完而不必一条一条地敲命令。
几种shell
bash默认shashcshksh
# 变量
变量名和等号之间不能有空格
首个字符必须为字母a-zA-Z
中间不能有空格可以使用下划线_
不能使用标点符号。
不能使用bash里的关键字可用help命令查看保留关键字
使用变量,只要在变量名前面加美元符号(`$`)即可
`{}`是为了帮助解释器识别变量的边界,推荐给所有变量引用加上花括号
使用 `readonly` 命令可以将变量定义为`只读`变量,只读变量的值不能被改变。
定义变量:`variableName="value"`
使用 `unset` 命令可以`删除`变量
## 变量类型
运行shell时会同时存在三种变量
1) 局部变量
局部变量在脚本或命令中定义仅在当前shell实例中有效其他shell启动的程序不能访问局部变量。
2) 环境变量
所有的程序包括shell启动的程序都能访问环境变量有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
3) shell变量
shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量有一部分是局部变量这些变量保证了shell的正常运行
## 特殊变量
`$0` 当前脚本的文件名
`$n` 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是`$1`,第二个参数是`$2`
`$# ` 传递给脚本或函数的参数个数。
`$*` 传递给脚本或函数的所有参数。
`$@` 传递给脚本或函数的所有参数。被双引号(" ")包含时,与 `$*` 稍有不同,下面将会讲到。
`$?` 上个命令的退出状态,或函数的返回值,大部分命令执行成功会返回 0失败返回 1不过也有一些命令返回其他值表示不同类型的错误。
`$$` 当前Shell进程ID。对于 Shell 脚本就是这些脚本所在的进程ID。
`$*``$@` 的区别:`$*``$@` 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"`$1`" "`$2`" … "`$n`" 的形式输出所有参数;但是当它们被双引号(" ")包含时,"`$*`" 会将所有的参数作为一个整体,以"`$1` `$2``$n`"的形式输出所有参数;"`$@`" 会将各个参数分开,以"`$1`" "`$2`" … "`$n`" 的形式输出所有参数。
# 替换
转义字符
`\\` 反斜杠
`\a` 警报,响铃
`\b` 退格(删除键)
`\f` 换页(FF),将当前位置移到下页开头
`\n` 换行
`\r` 回车
`\t` 水平制表符tab键
`\v` 垂直制表符
-e 表示对转义字符进行替换
-E 选项禁止转义
-n 选项可以禁止插入换行符
## 命令替换
命令替换是指Shell可以先执行命令将输出结果暂时保存在适当的地方输出。
~~~
# !/bin/bash
DATE=`date`
echo "Date is $DATE"
USERS=`who | wc -l`
echo "Logged in user are $USERS"
UP=`date ; uptime`
echo "Uptime is $UP"
~~~
## 变量替换
变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值
`${var}` 变量本来的值
`${var:-word}` 如果变量 var 为空或已被删除(unset),那么返回 word但不改变 var 的值。
`${var:=word}` 如果变量 var 为空或已被删除(unset),那么返回 word并将 var 的值设置为 word。
`${var:?message}` 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中那么脚本将停止运行。
`${var:+word}` 如果变量 var 被定义,那么返回 word但不改变 var 的值。
# 运算符
原生bash不支持简单的数学运算但是可以通过其他命令来实现例如 awk 和 exprexpr 最常用。
expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
~~~
# !/bin/bash
val=`expr 2 + 2`
echo "Total value : $val"
~~~
* 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2这与我们熟悉的大多数编程语言不一样。
* 完整的表达式要被 \` \` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。
## 算术运算符
`+` 加法 `expr $a + $b`
`-` 减法 `expr $a - $b`
`*` 乘法 `expr $a \* $b`
`/` 除法 `expr $b / $a`
`%` 取余 `expr $b % $a`
`=` 赋值
`==` 相等。
`!=` 不相等。
乘号(*)前边必须加反斜杠(\)才能实现乘法运算
## 关系运算符
`-eq` 检测两个数是否相等,相等返回 true。
`-ne` 检测两个数是否相等,不相等返回 true。
`-gt` 检测左边的数是否大于右边的,如果是,则返回 true。
`-lt` 检测左边的数是否小于右边的,如果是,则返回 true。
`-ge` 检测左边的数是否大等于右边的,如果是,则返回 true。
`-le` 检测左边的数是否小于等于右边的,如果是,则返回 true。
## 布尔运算符
`!` 非运算,表达式为 true 则返回 false否则返回 true。
`-o` 或运算,有一个表达式为 true 则返回 true。
`-a` 与运算,两个表达式都为 true 才返回 true。
## 字符串运算符
`=` 检测两个字符串是否相等,相等返回 true。
`!=` 检测两个字符串是否相等,不相等返回 true。
`-z str` 检测字符串长度是否为0为0返回 true。
`-n str` 检测字符串长度是否不为0不为0返回 true。
`str` 检测字符串是否为空,不为空返回 true。
## 文件测试运算符
文件测试运算符用于检测 Unix 文件的各种属性。
`-b file` 检测文件是否是块设备文件,如果是,则返回 true。
`-c file` 检测文件是否是字符设备文件,如果是,则返回 true。
`-d file` 检测文件是否是目录,如果是,则返回 true。
`-f file` 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
`-g file` 检测文件是否设置了 SGID 位,如果是,则返回 true。
`-k file` 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。
`-p file` 检测文件是否是具名管道,如果是,则返回 true。
`-u file` 检测文件是否设置了 SUID 位,如果是,则返回 true。
`-r file` 检测文件是否可读,如果是,则返回 true。
`-w file` 检测文件是否可写,如果是,则返回 true。
`-x file` 检测文件是否可执行,如果是,则返回 true。
`-s file` 检测文件是否为空文件大小是否大于0不为空返回 true。
`-e file` 检测文件(包括目录)是否存在,如果是,则返回 true。
# 注释
以“# ”开头的行就是注释,会被解释器忽略。
sh里没有多行注释只能每一行加一个# 号。可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
# 字符串
单引号字符串的限制:
* 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
* 单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
双引号的优点:
* 双引号里可以有变量
* 双引号里可以出现转义字符
拼接字符串
~~~
your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
~~~
获取字符串长度
~~~
string="abcd"
echo ${# string} # 输出 4
~~~
提取子字符串
~~~
string="alibaba is a great company"
echo ${string:1:4} # 输出liba
~~~
查找子字符串
`expr index String1 String2` 返回 String1 中包含 String2 中任意字符的第一个位置。从1开始数
~~~
string="alibaba is a great company"
echo `expr index "$string" is`
~~~
# 数组
bash支持一维数组不支持多维数组并且没有限定数组的大小。类似与C语言数组元素的下标由0开始编号。获取数组中的元素要利用下标下标可以是整数或算术表达式其值应大于或等于0。
## 定义
在Shell中用括号来表示数组数组元素用“空格”符号分割开。定义数组的一般形式为array_name=(value1 ... valuen)
~~~
array_name=(value0 value1 value2 value3)
array_name=(
value0
value1
value2
value3
)
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
~~~
## 读取
~~~
valuen=${array_name[2]}
~~~
使用`@``*` 可以获取数组中的所有元素
~~~
${array_name[*]}
${array_name[@]}
~~~
## 长度
~~~
# 取得数组元素的个数
length=${# array_name[@]}
# 或者
length=${# array_name[*]}
# 取得数组单个元素的长度
lengthn=${# array_name[n]}
~~~
# echo
echo是Shell的一个内部指令用于在屏幕上打印出指定的字符串。
`-n` 不要在最后自动换行
`-e` 若字符串中出现以下字符,则特别加以处理,而不会将它当成一般
文字输出:
>`\a` 发出警告声;
>
>`\b` 删除前一个字符;
>
>`\c` 最后不加上换行符号;
>
>`\f` 换行但光标仍旧停留在原来的位置;
>
>`\n` 换行且光标移至行首;
>
>`\r` 光标移至行首,但不换行;
>
>`\t` 插入tab
>
>`\v` 与\f相同
>
>`\\` 插入\字符;
>
>`\nnn` 插入nnn八进制所代表的ASCII字符
`help` 显示帮助
`version` 显示版本信息
## 原样输出字符串
若需要原样输出字符串(不进行转义),请使用单引号
~~~
echo '$name\"'
~~~
## 显示命令执行结果
~~~
echo `date`
~~~
# printf
printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。
注意printf 由 POSIX 标准所定义,移植性要比 echo 好。
printf 不像 echo 那样会自动换行,必须显式添加换行符(\n)。
~~~
$printf "Hello, Shell\n"
Hello, Shell
$
~~~
printf 命令的语法
~~~
printf format-string [arguments...]
~~~
format-string 为格式控制字符串arguments 为参数列表。
这里仅说明与C语言printf()函数的不同:
* printf 命令不用加括号
* format-string 可以没有引号,但最好加上,单引号双引号均可。
* 参数多于格式控制符(%)时format-string 可以重用,可以将所有参数都转换。
* arguments 使用空格分隔,不用逗号。
注意根据POSIX标准浮点格式%e、%E、%f、%g与%G是“不需要被支持”。这是因为awk支持浮点预算且有它自己的printf语句。这样Shell程序中需要将浮点数值进行格式化的打印时可使用小型的awk程序实现。然而内建于bash、ksh93和zsh中的printf命令都支持浮点格式。
# 分支
Shell 有三种 if ... else 语句:
* if ... fi 语句;
* if ... else ... fi 语句;
* if ... elif ... else ... fi 语句。
最后必须以 fi 来结尾闭合 iffi 就是 if 倒过来拼写,后面也会遇见。
注意expression 和方括号([ ])之间必须有空格,否则会有语法错误。
## if
~~~
if [ expression ]
then
statements
fi
~~~
## if else
~~~
if [ expression ]
then
Statements
else
Statements
fi
~~~
## if elif fi
~~~
if [ expression 1 ]
then
Statements
elif [ expression 2 ]
then
Statements
elif [ expression 3 ]
then
Statements
else
Statements
fi
~~~
if ... else 语句也可以写成一行,以命令的方式来运行,经常与 test 命令结合使用
`test` 命令用于检查某个条件是否成立,与方括号([ ])类似。
~~~
if test $[2*3] -eq $[1+5]; then echo 'The two numbers are equal!'; fi;
~~~
# 多分枝选择
case ... esac 与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构。
case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。
~~~
case 值 in
模式1)
command1
command2
command3
;;
模式2)
command1
command2
command3
;;
*)
command1
command2
command3
;;
esac
~~~
取值后面必须为关键字 `in`
每一模式必须以`)`结束
`;;` 与其他语言中的 break 类似
~~~
# !/bin/bash
option="${1}"
case ${option} in
-f) FILE="${2}"
echo "File name is $FILE"
;;
-d) DIR="${2}"
echo "Dir name is $DIR"
;;
*)
echo "`basename ${0}`:usage: [-f file] | [-d directory]"
exit 1 # Command to come out of the program with status 1
;;
esac
~~~
# 循环
## for
~~~
for 变量 in 列表
do
command1
command2
...
commandN
done
~~~
列表是一组值(数字、字符串等)组成的序列,每个值通过空格分隔。每循环一次,就将列表中的下一个值赋给变量。
~~~
# !/bin/bash
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
~~~
~~~
# !/bin/bash
for FILE in $HOME/.bash*
do
echo $FILE
done
~~~
## while
~~~
while command
do
statements
done
~~~
~~~
COUNTER=0
while [ $COUNTER -lt 5 ]
do
COUNTER=`expr $COUNTER+1`
echo $COUNTER
done
~~~
while循环可用于读取键盘信息
~~~
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
echo "Yeah! great film the $FILM"
done
~~~
## until
until 循环执行一系列命令直至条件为 `true``停止`。until 循环与 while 循环在处理方式上刚好相反。一般while循环优于until循环但在某些时候也只是极少数情况下until 循环更加有用。
~~~
until command
do
statements
done
~~~
## 跳出循环
### break
break将终止循环体中的后续操作
在嵌套循环中break 命令后面还可以跟一个整数,表示跳出第几层循环`break n`
### continue
continue跳出当次循环
同样continue 后面也可以跟一个数字,表示跳出第几层循环`continue n`
# 函数
## 定义
~~~
function_name () {
list of commands
[ return value ]
}
# 可以加上function 关键字
function function_name () {
list of commands
[ return value ]
}
# 函数返回值可以显式增加return语句如果不加会将最后一条命令运行结果作为返回值。
~~~
Shell 函数返回值只能是整数一般用来表示函数执行成功与否0表示成功其他值表示失败。如果 return 其他数据比如一个字符串往往会得到错误提示“numeric argument required”。
如果一定要让函数返回字符串,那么可以先定义一个变量,用来接收函数的计算结果,脚本在需要的时候访问这个变量来获得函数返回值。
~~~
# !/bin/bash
funWithReturn(){
echo "The function is to get the sum of two numbers..."
echo -n "Input first number: "
read aNum
echo -n "Input another number: "
read anotherNum
echo "The two numbers are $aNum and $anotherNum !"
return $(($aNum+$anotherNum))
# 两组括号的意思是做算术运算,如果不这么写直接写$a+$b的话是按字符串理解的就是25+50这个字符串
}
funWithReturn
# Capture value returnd by last command
ret=$?
echo "The sum of two numbers is $ret !"
~~~
调用函数只需要给出函数名,不需要加括号。
函数返回值在调用该函数后通过 `$?` 来获得。
## 嵌套
~~~
# !/bin/bash
# Calling one function from another
number_one () {
echo "Url_1 is http://see.xidian.edu.cn/cpp/shell/"
number_two
}
number_two () {
echo "Url_2 is http://see.xidian.edu.cn/cpp/u/xitong/"
}
number_one
~~~
## 删除
~~~
unset .f function_name
~~~
## 参数
在Shell中调用函数时可以向其传递参数。在函数体内部通过 `$n` 的形式来获取参数的值,例如,`$1`表示第一个参数,`$2`表示第二个参数...
~~~
# !/bin/bash
funWithParam(){
echo "The value of the first parameter is $1 !"
echo "The value of the second parameter is $2 !"
echo "The value of the tenth parameter is $10 !"
echo "The value of the tenth parameter is ${10} !"
echo "The value of the eleventh parameter is ${11} !"
echo "The amount of the parameters is $# !" # 参数个数
echo "The string of the parameters is $* !" # 传递给函数的所有参数
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
~~~
注意,`$10` 不能获取第十个参数,获取第十个参数需要`${10}`。当n>=10时需要使用`${n}`来获取参数。
特殊参数
`$# ` 传递给函数的参数个数。
`$*` 显示所有传递给函数的参数。
`$@``$*`相同但是略有区别请查看Shell特殊变量。
`$?` 函数的返回值。
# IO重定向
~~~
command > file
command < file
~~~
一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
* 标准输入文件(`stdin`)stdin的文件描述符为`0`Unix程序默认从stdin读取数据。
* 标准输出文件(`stdout`)stdout 的文件描述符为`1`Unix程序默认向stdout输出数据。
* 标准错误文件(`stderr`)stderr的文件描述符为`2`Unix程序会向stderr流中写入错误信息。
stderr 重定向到 file
~~~
command 2 > file
~~~
将 stdout 和 stderr 合并后重定向到 file
~~~
command > file 2>&1
~~~
默认情况下command > file 将 stdout 重定向到 filecommand < file 将stdin 重定向到 file。
`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 之间的内容作为输入。
## Here Document
~~~
command << delimiter
document
delimiter
~~~
它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
注意:
* 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
* 开始的delimiter前后的空格会被忽略掉。
## /dev/null
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到”禁止输出“的效果。
屏蔽 stdout 和 stderr
~~~
command > /dev/null 2>&1
~~~
# 文件包含
Shell 也可以包含外部脚本,将外部脚本的内容合并到当前脚本
~~~
. filename
# 或
source filename
~~~
两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格
脚本 subscript.sh
~~~
url="http://see.xidian.edu.cn/cpp/view/2738.html"
~~~
引入当前目录下的subscript.sh脚本
~~~
# !/bin/bash
. ./subscript.sh
echo $url
~~~