赞
踩
- 1、先查看脚本解释器
-
- [es@bigdata-senior01 ~]$ echo $SHELL
- /bin/bash
- 2、编写最简单的脚本
-
- vi test.sh
- #第一行的脚本声明(#!)用来告诉系统使用哪种 Shell 解释器来执行该脚本
- #!/bin/bash
- #查看当前目录,按文件大小列出文件
- pwd
- ls -lSh
- Shell 脚本文件的名称可以任意,但为了避免被误以为是普通文件,将.sh 后缀加上,以表示是一个脚本文件。
- 3、执行脚本的方式
-
- 第一种:用解释器执行,sh/bash或都可以,用subshell方式开打
- [es@bigdata-senior01 ~]$ bash test.sh
-
- 第二种:通过./方式直接执行,用subshell方式开打,需要有“执行权限”,通过chmod来添加
- [es@bigdata-senior01 ~]$ ./test.sh
-
- 第三种:用source来执行,在当前shell内去读取,变量会被设置到当前shell环境中
- [es@bigdata-senior01 ~]$ source test.sh
- source可以省略为"."
- [es@bigdata-senior01 ~]$ . test.sh #注意.后面有空格
-
-
-
-
- 注意:第二种方式如果出现权限不足的情况,那么注意看看文件有没有执行权限
-
- [es@bigdata-senior01 ~]$ ./test.sh
- -bash: ./test.sh: 权限不够
-
- 因为test.sh没有执行权限,我们查看一下
- [es@bigdata-senior01 ~]$ ll test.sh
- -rw------- 1 es es 167 1月 8 15:25 test.sh
-
- 没有x,没有执行权限,所以加一下就可以
- [es@bigdata-senior01 ~]$ chmod 700 test.sh
-
- 如果要给所有人权限
- [es@bigdata-senior01 ~]$ chmod 777 test.sh
- 4、脚本参数,例如:./test02.sh one two three four five
-
- $0 表示当前 Shell 脚本程序的名称
- $# 表示总共有几个参数
- $* 表示所有位置的参数值
- $? 表示shell返回值
- $1、$2、$3……则分别对应着第 N 个位置的参数值
- 用例
-
- [es@bigdata-senior01 ~]$ cat test02.sh
- #!/bin/bash
- echo "脚本名称:$0"
- echo "脚本共有参数$#个"
- echo "脚本参数:$*"
- echo "脚本第一个参数:$1"
- echo "脚本第三个参数:$3"
-
- [es@bigdata-senior01 ~]$ bash test02.sh one two three four five
- 脚本名称:test02.sh
- 脚本共有参数5个
- 脚本参数:one two three four five
- 脚本第一个参数:one
- 脚本第三个参数:three
- 5、条件测试,Shell 脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回其他随机数值。
-
- [ 条件表达式 ] #注意表达式两端各有一个空格
- 5.1 文件测试:
-
- -d 测试文件是否为目录类型
- -e 测试文件是否存在
- -f 判断是否为一般文件
- -r 测试当前用户是否有权限读取
- -w 测试当前用户是否有权限写入
- -x 测试当前用户是否有权限执行
- 用例:
-
- 判断test02.sh是否是目录,结果非0,为不符合
- 这个判断原则其实与程序(列入java程序)退出代码一致,退出代码为0表示正常退出,退出代码>0就是异常退出
- [es@bigdata-senior01 ~]$ [ -d test02.sh ]
- [es@bigdata-senior01 ~]$ echo $?
- 1
-
- 判断test02.sh是否是文件,结果为0,符合
- [es@bigdata-senior01 ~]$ [ -f test02.sh ]
- [es@bigdata-senior01 ~]$ echo $?
- 0
- 5.2 逻辑与(&&),在 Shell终端中逻辑“与”的运算符号是&&,它表示当前面的命令执行成功后才会执行它后面的命令
-
- [es@bigdata-senior01 ~]$ [ -f test02.sh ] && echo "ok"
- ok
- 5.3 逻辑或(||),表示当前面的命令执行失败后才会执行它后面的命令
-
- [es@bigdata-senior01 ~]$ [ -d test02.sh ] || echo "不是目录"
- 不是目录
-
- [es@bigdata-senior01 ~]$ [ $USER = 112 ] && echo "用户是es" || echo "用户不是es"
- 用户不是es
- [es@bigdata-senior01 ~]$ [ $USER = es ] && echo "用户是es" || echo "用户不是es"
- 用户是es
-
- 5.4 逻辑“非”(!),在 Linux系统中的运算符号是一个叹号(!),它表示把条件测试中的判断结果取相反值
-
- [es@bigdata-senior01 ~]$ [ ! $USER = es ] && echo "用户是es" || echo "用户不是es"
- 用户不是es
-
- 6、整数比较运算符,因为=,<,>这些符号和赋值以及重定向冲突,所以shell脚本中,整数比较使用英文简写来代替的
-
- -eq 是否等于
- -ne 是否不等于
- -gt 是否大于
- -lt 是否小于
- -le 是否等于或小于
- -ge 是否大于或等于
- 用例:
-
- 取空闲内存:
- [es@bigdata-senior01 ~]$ freeMem=$(free -m | grep Mem | awk '{print $4}')
- 或者
- [es@bigdata-senior01 ~]$ freeMem2=`free -m | grep Mem | awk '{print $4}'`
-
- [es@bigdata-senior01 ~]$ [ $freeMem -lt 1024 ] && echo "内存不足" || echo "内存正常"
- 内存正常
- [es@bigdata-senior01 ~]$ echo $freeMem
- 1552
- 7、字符串比较,只有等于、不等于、空等。
-
- = 比较字符串内容是否相同
- != 比较字符串内容是否不同
- -z 判断字符串内容是否为空
- [es@bigdata-senior01 ~]$ [ $LANG != "zh_CN.UTF-8" ] && echo "需要中文字符集" || echo "是中文字符集"
- 是中文字符集
- 8、流程控制,if、for、while、case等
-
- 8.1 if条件
-
- 语法:
- if 条件
- then 命令1
- else 命令2
- fi 结束if
-
- 语法2:
- if 条件
- then 命令1
- elif 条件2
- then 命令2
- else
- 命令3
- fi 结束if
- 用例1:vi createHomeData.sh
-
- #!/bin/bash
- #在用户home目录下,如果不存在data目录就创建data目录,然后拷贝数据到该目录
- dir=${HOME}"/data" #字符串连接,将$HOME变量和字符串"/data"连接
- if [ ! -e $dir ]
- then
- echo "目录$dir不存在,创建..."
- mkdir -p $dir
- cp /opt/elasticsearch*/config/*.yml $dir
- else
- cp /opt/elasticsearch*/config/*.yml $dir
- fi
- ls -d $dir
- ls -l $dir
- 用例2:vi checkHost.sh
-
- #!/bin/bash
- #检查参数1所表示的主机是否在线
- if [ -z $1 ]
- then
- echo "请输入主机IP或名字"
- exit
- fi
- echo "正在连接..."
- ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
- if [ $? -eq 0 ]
- then
- echo "Host1在线"
- else
- echo "Host1不在线"
- fi
- [es@bigdata-senior01 ~]$ ./checkHost.sh www.google.com
- 正在连接...
- Host:www.google.com不在线
- [es@bigdata-senior01 ~]$ ./checkHost.sh 114.114.114.114
- 正在连接...
- Host:114.114.114.114在线
- [es@bigdata-senior01 ~]$ ./checkHost.sh
- 请输入主机IP或名字
- 检查端口是否开放
-
- #!/bin/bash
- if [ -z $1 ] ; then
- echo "请输入需要检测IP和端口文件"
- exit
- fi
-
- if [ ! -e $1 ] ; then
- echo "文件不存在,请重新确认"
- exit
- fi
-
- File=$1
-
- cat $File | while read host
- do
- nc -zvw5 $host &> /dev/null
- if [ $? -eq 0 ] ; then echo "host: $host 开放"
- else echo "host: $host 关闭"
- fi
-
- done
-
-
- 用例3: vi checkScores.sh
-
- #!/bin/bash
- #输入分数,判断分数等级
- read -p "输入分数0-100: " grade
- if [ $grade -ge 90 ] && [ $grade -le 100 ] ; then
- echo "grade A"
- elif [ $grade -ge 75 ] && [ $grade -lt 90 ] ; then
- echo "grade B"
- elif [ $grade -ge 60 ] && [ $grade -lt 75 ] ; then
- echo "grade C"
- else
- echo "grade D"
- fi
- 8.2 for循环
-
- 语法:
- for 被循环自动赋值的变量 in 取值列表
- do
- 执行各种命令
- done
- 用例1:vi createUsers.sh
-
- 先准备一个userlist.txt,里面加一下用户名称,如下:
- [es@bigdata-senior01 ~]$ cat userlist.txt
- tom
- amy
- xu.dm
- xu.dm.test
- bus
- dba
- 编辑createUsers.sh脚本
-
- #!/bin/bash
- # 用重定向到/dev/nul来屏蔽屏幕输出
- # 脚本后面需要一个文件名参数
- if [ -z $1 ] ; then
- echo "请在脚本后面输入用户文件列表"
- exit
- else
- if [ ! -e $1 ] ; then
- echo "输入的文件不存在,请检查后重新输入"
- exit
- fi
- fi
-
- read -p "请输入用户密码:" password
- users=$(cat $1) #注意等号两边不能有空格,shell语法坑死人
- echo $users
- for uname in $users
- do
- id $uname &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "$uname,用户已经存在!"
- else
- useradd $uname &> /dev/null
- echo "$password" | passwd --stdin $uname &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "$uname,创建成功"
- else
- echo "$uname,创建失败"
- fi
- fi
-
- done
- 执行脚本:
- [es@bigdata-senior01 ~]$ sudo ./createUsers.sh userlist.txt
- 请输入用户密码:123@abc.com
- tom amy xu.dm xu.dm.test bus dba
- tom,创建成功
- amy,创建成功
- xu.dm,用户已经存在!
- xu.dm.test,创建成功
- bus,创建成功
- dba,创建成功
- 用例2:vi checkHosts.sh,检查文件列表里的主机是否在线
-
- #!/bin/bash
- if [ -z $1 ] ; then
- echo "请输入主机列表文件"
- exit
- fi
-
- if [ ! -e $1 ] ; then
- echo "文件不存在,请检查后重新输入"
- exit
- fi
-
- hostlist=$(cat $1)
- for host in $hostlist ; do
- ping -c 3 -i 0.2 -W 3 $host &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "hosthost 在线"
- else
- echo "hosthost 不在线或被屏蔽"
- fi
-
- done
- [es@bigdata-senior01 ~]$ ./checkHosts.sh hosts.txt
- host:192.168.1.2 在线
- host:www.baidu.com 在线
- host:www.google.com 不在线或被屏蔽
- host:www.cnblogs.cn 在线
- host:www.csdn.net 在线
- host:www.qq.com 在线
- 8.3 while语句
-
- 语法:
- while 条件测试语句
- do
- 执行命令
- done
- 用例1:vi outputNum.sh
-
- #!/bin/bash
- #倒序输出数字
- if [ -z $1 ] ; then
- echo "please input a integer number[1-100]"
- exit
- fi
-
- if [ $1 -gt 100 ] || [ $1 -lt 1 ] ; then
- echo "error num,need number between [1-100]"
- exit
- fi
- num=$1
- while [ $num -ge 1 ] ; do
- echo "numnum"
- #num-1有三种写法
- #let "num=$num - 1"
- #num=$(expr $num - 1)
- let num--
- done
- 用例2:
-
- #!/bin/bash
- #测试随机数,需要输入测试次数
- if [ -z $1 ] ; then
- echo "please input test count number"
- exit
- fi
-
- count=$1
- num=1
- while [ $count -gt 0 ]
- do
- echo "第$num次,随机数:$RANDOM"
- let num++
- let count--
- done
- 8.4、case语句
-
- case 变量值 in
- 模式1)
- 命令1;;
- 模式2)
- 命令2;;
- *)
- 其他命令;;
- esac #就是case反过来和if语句类似,我已经不想吐槽这是什么逻辑
- 模式字符串中可以使用通配符
- 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令列表。
- 各模式字符串应是唯一的,不应重复出现,并且要合理安排它们的出现顺序。
-
-
- 用例1:修改上面的testRandom.sh
-
- #!/bin/bash
- #测试随机数,需要输入测试次数
- if [ -z $1 ] ; then
- echo "please input test count number"
- exit
- fi
-
- case $1 in
- *[a-z]* | *[A-Z]*)
- echo "输入的是字母,需要输入数字"
- exit;;
- *[0-9]*)
- echo "输入的是数字,符合要求";;
- *)
- echo "输入非法字符无法继续"
- exit;;
- esac
-
- count=$1
- num=1
- while [ $count -gt 0 ]
- do
- echo "第$num次,随机数:$RANDOM"
- let num++
- let count--
- done
- 9、break和continue
-
- break:用于立即终止当前循环的执行,break命令可以使用户从循环体中退出来。
- 语法:break[n] ,其中,n表示要跳出几层循环,默认值为1
-
- continue:跳过循环体中在其之后的语句,会返回到本循环层的开头,进行下一次循环。
- –语法:continue[n],其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环,默认值为1,循环层数是由内向外编号。
-
-
- 假设我们定义了一个变量为:
- file=/dir1/dir2/dir3/my.file.txt
-
- 可以用${ }分别替换得到不同的值:
- ${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
- ${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
- ${file#*.}:删掉第一个 . 及其左边的字符串:file.txt
- ${file##*.}:删掉最后一个 . 及其左边的字符串:txt
- ${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3,#取父件夹路径
- ${file%%/*}:删掉第一个 / 及其右边的字符串:(空值)
- ${file%.*}:删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
- ${file%%.*}:删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
-
- 记忆的方法为:
- # 是 去掉左边(键盘上#在 $ 的左边)
- %是去掉右边(键盘上% 在$ 的右边)
- 单一符号是最小匹配;两个符号是最大匹配
- ${file:0:5}:提取最左边的 5 个字节:/dir1
- ${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
- 也可以对变量值里的字符串作替换:
- ${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt
- ${file//dir/path}:将全部dir 替换为 path:/path1/path2/path3/my.file.txt
-
- 利用 ${ } 还可针对不同的变数状态赋值(沒设定、空值、非空值):
- ${file-my.file.txt} :假如 $file 沒有设定,則使用 my.file.txt 作传回值。(空值及非空值時不作处理)
- ${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作处理)
- ${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作处理)
- ${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作处理)
- ${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。(空值及非空值時不作处理)
- ${file:=my.file.txt} :若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為my.file.txt 。 (非空值時不作处理)
- ${file?my.file.txt} :若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
-
- ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值時不作处理)
- ${#var} 可计算出变量值的长度:
-
- ${#file} 可得到 27 ,因为/dir1/dir2/dir3/my.file.txt 是27个字节
- ✴️✴️✴️✴️Shell 数组
- bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
-
- 类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
-
- 定义数组
- 在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
-
- 数组名=(值1 值2 ... 值n)
- 例如:
-
- array_name=(value0 value1 value2 value3)
- 或者
-
- array_name=(
- value0
- value1
- value2
- value3
- )
- 还可以单独定义数组的各个分量:
-
- array_name[0]=value0
- array_name[1]=value1
- array_name[n]=valuen
- 可以不使用连续的下标,而且下标的范围没有限制。
-
- 读取数组
- 读取数组元素值的一般格式是:
-
- ${数组名[下标]}
- 例如:
-
- valuen=${array_name[n]}
- 使用 @ 符号可以获取数组中的所有元素,例如:
-
- echo ${array_name[@]}
- 获取数组的长度
- 获取数组长度的方法与获取字符串长度的方法相同,例如:
-
- # 取得数组元素的个数
- length=${#array_name[@]}
- # 或者
- length=${#array_name
- }
- # 取得数组单个元素的长度
- lengthn=${#array_name[n]}
- Shell 注释
- 以 # 开头的行就是注释,会被解释器忽略。
-
- 通过每一行加一个 # 号设置多行注释,像这样:
-
- #--------------------------------------------
- # 这是一个注释
- # author:菜鸟教程
- # site:www.runoob.com
- # slogan:学的不仅是技术,更是梦想!
- #--------------------------------------------
- ##### 用户配置区 开始 #####
- #
- #
- # 这里可以添加脚本描述信息
- #
- #
- ##### 用户配置区 结束 #####
- 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
-
- 每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
-
- 多行注释
- 多行注释还可以使用以下格式:
-
- :<<EOF
- 注释内容...
- 注释内容...
- 注释内容...
- EOF
- EOF 也可以使用其他符号:
-
- :<<'
- 注释内容...
- 注释内容...
- 注释内容...
- '
-
- :<<!
- 注释内容...
- 注释内容...
- 注释内容...
- !
- ✴️✴️✴️✴️✴️函数,函数必须写在调用的前面
- 实例
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- funWithReturn(){
- echo "这个函数会对输入的两个数字进行相加运算..."
- echo "输入第一个数字: "
- read aNum
- echo "输入第二个数字: "
- read anotherNum
- echo "两个数字分别为 $aNum 和 $anotherNum !"
- return $(($aNum+$anotherNum))
- }
- funWithReturn
- echo "输入的两个数字之和为 $? !"
- 输出类似下面:
-
- 这个函数会对输入的两个数字进行相加运算...
- 输入第一个数字:
- 1
- 输入第二个数字:
- 2
- 两个数字分别为 1 和 2 !
- 输入的两个数字之和为 3 !
- 函数返回值在调用该函数后通过 $? 来获得。
-
- 注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
-
- 函数参数
- 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
-
- 带参数的函数示例:
-
- 实例
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- funWithParam(){
- echo "第一个参数为 $1 !"
- echo "第二个参数为 $2 !"
- echo "第十个参数为 $10 !"
- echo "第十个参数为 ${10} !"
- echo "第十一个参数为 ${11} !"
- echo "参数总数有 $# 个!"
- echo "作为一个字符串输出所有参数 $* !"
- }
- funWithParam 1 2 3 4 5 6 7 8 9 34 73
- 输出结果:
-
- 第一个参数为 1 !
- 第二个参数为 2 !
- 第十个参数为 10 !
- 第十个参数为 34 !
- 第十一个参数为 73 !
- 参数总数有 11 个!
- 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
- 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
-
- 另外,还有几个特殊字符用来处理参数:
-
- 参数处理 说明
- $# 传递到脚本或函数的参数个数
- $* 以一个单字符串显示所有向脚本传递的参数
- $$ 脚本运行的当前进程ID号
- $! 后台运行的最后一个进程的ID号
- $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
- $- 显示Shell使用的当前选项,与set命令功能相同。
- $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
- 
- 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 之间的内容作为输入。
- 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
-
- 输出重定向
- 重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:
-
- command1 > file1
- 上面这个命令执行command1然后将输出的内容存入file1。
-
- 注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
-
- 实例
- 执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
-
- $ who > users
- 执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
-
- 你可以使用 cat 命令查看文件内容:
-
- $ cat users
- _mbsetupuser console Oct 31 17:35
- tianqixin console Oct 31 17:35
- tianqixin ttys000 Dec 1 11:33
- 输出重定向会覆盖文件内容,请看下面的例子:
-
- $ echo "菜鸟教程:www.runoob.com" > users
- $ cat users
- 菜鸟教程:www.runoob.com
- $
- 如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
-
- $ echo "菜鸟教程:www.runoob.com" >> users
- $ cat users
- 菜鸟教程:www.runoob.com
- 菜鸟教程:www.runoob.com
- $
- 输入重定向
- 和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:
-
- command1 < file1
- 这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
-
- 注意:输出重定向是大于号(>),输入重定向是小于号(<)。
-
- 实例
- 接着以上实例,我们需要统计 users 文件的行数,执行以下命令:
-
- $ wc -l users
- 2 users
- 也可以将输入重定向到 users 文件:
-
- $ wc -l < users
- 2
- 注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
-
- command1 < infile > outfile
- 同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
-
- 重定向深入讲解
- 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
-
- 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
- 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
- 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
- 默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
-
- 如果希望 stderr 重定向到 file,可以这样写:
-
- $ command 2>file
- 如果希望 stderr 追加到 file 文件末尾,可以这样写:
-
- $ command 2>>file
- 2 表示标准错误文件(stderr)。
-
- 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
-
- $ command > file 2>&1
-
- 或者
-
- $ command >> file 2>&1
- 如果希望对 stdin 和 stdout 都重定向,可以这样写:
-
- $ command < file1 >file2
- command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
-
- Here Document
- Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
-
- 它的基本的形式如下:
-
- command << delimiter
- document
- delimiter
- 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
-
- 注意:
-
- 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
- 开始的delimiter前后的空格会被忽略掉。
- 实例
- 在命令行中通过 wc -l 命令计算 Here Document 的行数:
-
- $ wc -l << EOF
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- EOF
- 3 # 输出结果为 3 行
- $
- 我们也可以将 Here Document 用在脚本中,例如:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- cat << EOF
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- EOF
- 执行以上脚本,输出结果:
-
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- /dev/null 文件
- 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
-
- $ command > /dev/null
- /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
-
- 如果希望屏蔽 stdout 和 stderr,可以这样写:
-
- $ command > /dev/null 2>&1
- 注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
-
- 这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
- Shell 文件包含
- 和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
-
- Shell 文件包含的语法格式如下:
-
- . filename # 注意点号(.)和文件名中间有一空格
-
- 或
-
- source filename
- 实例
- 创建两个 shell 脚本文件。
-
- test1.sh 代码如下:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- url="http://www.runoob.com"
- test2.sh 代码如下:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- #使用 . 号来引用test1.sh 文件
- . ./test1.sh
-
- # 或者使用以下包含文件代码
- # source ./test1.sh
-
- echo "菜鸟教程官网地址:$url"
- 接下来,我们为 test2.sh 添加可执行权限并执行:
-
- $ chmod +x test2.sh
- $ ./test2.sh
- 菜鸟教程官网地址:http://www.runoob.com
- 注:被包含的文件 test1.sh 不需要可执行权限。
- 默认 bash 里定义的变量是全局的。
- $ a=10; function b() { a=2; }; b; echo $a
- 执行结果为2
- 即:函数b里对a进行修改后,a的值就发生改变。
- 如果不想b对a的操作不影响全局的值,可以将b中的a设为局部变量。
- 如下所示:
- $ a=10; function b() { local a=2; }; b; echo $a
- 执行结果为10
- 还有一点需要注意。
- 在pipe之后的处理不会改变原来的值,因为新建一个进程。
- $ a=1; ec1、先查看脚本解释器
-
- [es@bigdata-senior01 ~]$ echo $SHELL
- /bin/bash
- 2、编写最简单的脚本
-
- vi test.sh
- #第一行的脚本声明(#!)用来告诉系统使用哪种 Shell 解释器来执行该脚本
- #!/bin/bash
- #查看当前目录,按文件大小列出文件
- pwd
- ls -lSh
- Shell 脚本文件的名称可以任意,但为了避免被误以为是普通文件,将.sh 后缀加上,以表示是一个脚本文件。
- 3、执行脚本的方式
-
- 第一种:用解释器执行,sh/bash或都可以,用subshell方式开打
- [es@bigdata-senior01 ~]$ bash test.sh
-
- 第二种:通过./方式直接执行,用subshell方式开打,需要有“执行权限”,通过chmod来添加
- [es@bigdata-senior01 ~]$ ./test.sh
-
- 第三种:用source来执行,在当前shell内去读取,变量会被设置到当前shell环境中
- [es@bigdata-senior01 ~]$ source test.sh
- source可以省略为"."
- [es@bigdata-senior01 ~]$ . test.sh #注意.后面有空格
-
-
-
-
- 注意:第二种方式如果出现权限不足的情况,那么注意看看文件有没有执行权限
-
- [es@bigdata-senior01 ~]$ ./test.sh
- -bash: ./test.sh: 权限不够
-
- 因为test.sh没有执行权限,我们查看一下
- [es@bigdata-senior01 ~]$ ll test.sh
- -rw------- 1 es es 167 1月 8 15:25 test.sh
-
- 没有x,没有执行权限,所以加一下就可以
- [es@bigdata-senior01 ~]$ chmod 700 test.sh
-
- 如果要给所有人权限
- [es@bigdata-senior01 ~]$ chmod 777 test.sh
- 4、脚本参数,例如:./test02.sh one two three four five
-
- $0 表示当前 Shell 脚本程序的名称
- $# 表示总共有几个参数
- $* 表示所有位置的参数值
- $? 表示shell返回值
- $1、$2、$3……则分别对应着第 N 个位置的参数值
- 用例
-
- [es@bigdata-senior01 ~]$ cat test02.sh
- #!/bin/bash
- echo "脚本名称:$0"
- echo "脚本共有参数$#个"
- echo "脚本参数:$*"
- echo "脚本第一个参数:$1"
- echo "脚本第三个参数:$3"
-
- [es@bigdata-senior01 ~]$ bash test02.sh one two three four five
- 脚本名称:test02.sh
- 脚本共有参数5个
- 脚本参数:one two three four five
- 脚本第一个参数:one
- 脚本第三个参数:three
- 5、条件测试,Shell 脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字 0,否则便返回其他随机数值。
-
- [ 条件表达式 ] #注意表达式两端各有一个空格
- 5.1 文件测试:
-
- -d 测试文件是否为目录类型
- -e 测试文件是否存在
- -f 判断是否为一般文件
- -r 测试当前用户是否有权限读取
- -w 测试当前用户是否有权限写入
- -x 测试当前用户是否有权限执行
- 用例:
-
- 判断test02.sh是否是目录,结果非0,为不符合
- 这个判断原则其实与程序(列入java程序)退出代码一致,退出代码为0表示正常退出,退出代码>0就是异常退出
- [es@bigdata-senior01 ~]$ [ -d test02.sh ]
- [es@bigdata-senior01 ~]$ echo $?
- 1
-
- 判断test02.sh是否是文件,结果为0,符合
- [es@bigdata-senior01 ~]$ [ -f test02.sh ]
- [es@bigdata-senior01 ~]$ echo $?
- 0
- 5.2 逻辑与(&&),在 Shell终端中逻辑“与”的运算符号是&&,它表示当前面的命令执行成功后才会执行它后面的命令
-
- [es@bigdata-senior01 ~]$ [ -f test02.sh ] && echo "ok"
- ok
- 5.3 逻辑或(||),表示当前面的命令执行失败后才会执行它后面的命令
-
- [es@bigdata-senior01 ~]$ [ -d test02.sh ] || echo "不是目录"
- 不是目录
-
- [es@bigdata-senior01 ~]$ [ $USER = 112 ] && echo "用户是es" || echo "用户不是es"
- 用户不是es
- [es@bigdata-senior01 ~]$ [ $USER = es ] && echo "用户是es" || echo "用户不是es"
- 用户是es
-
- 5.4 逻辑“非”(!),在 Linux系统中的运算符号是一个叹号(!),它表示把条件测试中的判断结果取相反值
-
- [es@bigdata-senior01 ~]$ [ ! $USER = es ] && echo "用户是es" || echo "用户不是es"
- 用户不是es
-
- 6、整数比较运算符,因为=,<,>这些符号和赋值以及重定向冲突,所以shell脚本中,整数比较使用英文简写来代替的
-
- -eq 是否等于
- -ne 是否不等于
- -gt 是否大于
- -lt 是否小于
- -le 是否等于或小于
- -ge 是否大于或等于
- 用例:
-
- 取空闲内存:
- [es@bigdata-senior01 ~]$ freeMem=$(free -m | grep Mem | awk '{print $4}')
- 或者
- [es@bigdata-senior01 ~]$ freeMem2=`free -m | grep Mem | awk '{print $4}'`
-
- [es@bigdata-senior01 ~]$ [ $freeMem -lt 1024 ] && echo "内存不足" || echo "内存正常"
- 内存正常
- [es@bigdata-senior01 ~]$ echo $freeMem
- 1552
- 7、字符串比较,只有等于、不等于、空等。
-
- = 比较字符串内容是否相同
- != 比较字符串内容是否不同
- -z 判断字符串内容是否为空
- [es@bigdata-senior01 ~]$ [ $LANG != "zh_CN.UTF-8" ] && echo "需要中文字符集" || echo "是中文字符集"
- 是中文字符集
- 8、流程控制,if、for、while、case等
-
- 8.1 if条件
-
- 语法:
- if 条件
- then 命令1
- else 命令2
- fi 结束if
-
- 语法2:
- if 条件
- then 命令1
- elif 条件2
- then 命令2
- else
- 命令3
- fi 结束if
- 用例1:vi createHomeData.sh
-
- #!/bin/bash
- #在用户home目录下,如果不存在data目录就创建data目录,然后拷贝数据到该目录
- dir=${HOME}"/data" #字符串连接,将$HOME变量和字符串"/data"连接
- if [ ! -e $dir ]
- then
- echo "目录$dir不存在,创建..."
- mkdir -p $dir
- cp /opt/elasticsearch*/config/*.yml $dir
- else
- cp /opt/elasticsearch*/config/*.yml $dir
- fi
- ls -d $dir
- ls -l $dir
- 用例2:vi checkHost.sh
-
- #!/bin/bash
- #检查参数1所表示的主机是否在线
- if [ -z $1 ]
- then
- echo "请输入主机IP或名字"
- exit
- fi
- echo "正在连接..."
- ping -c 3 -i 0.2 -W 3 $1 &> /dev/null
- if [ $? -eq 0 ]
- then
- echo "Host1在线"
- else
- echo "Host1不在线"
- fi
- [es@bigdata-senior01 ~]$ ./checkHost.sh www.google.com
- 正在连接...
- Host:www.google.com不在线
- [es@bigdata-senior01 ~]$ ./checkHost.sh 114.114.114.114
- 正在连接...
- Host:114.114.114.114在线
- [es@bigdata-senior01 ~]$ ./checkHost.sh
- 请输入主机IP或名字
- 检查端口是否开放
-
- #!/bin/bash
- if [ -z $1 ] ; then
- echo "请输入需要检测IP和端口文件"
- exit
- fi
-
- if [ ! -e $1 ] ; then
- echo "文件不存在,请重新确认"
- exit
- fi
-
- File=$1
-
- cat $File | while read host
- do
- nc -zvw5 $host &> /dev/null
- if [ $? -eq 0 ] ; then echo "host: $host 开放"
- else echo "host: $host 关闭"
- fi
-
- done
-
-
- 用例3: vi checkScores.sh
-
- #!/bin/bash
- #输入分数,判断分数等级
- read -p "输入分数0-100: " grade
- if [ $grade -ge 90 ] && [ $grade -le 100 ] ; then
- echo "grade A"
- elif [ $grade -ge 75 ] && [ $grade -lt 90 ] ; then
- echo "grade B"
- elif [ $grade -ge 60 ] && [ $grade -lt 75 ] ; then
- echo "grade C"
- else
- echo "grade D"
- fi
- 8.2 for循环
-
- 语法:
- for 被循环自动赋值的变量 in 取值列表
- do
- 执行各种命令
- done
- 用例1:vi createUsers.sh
-
- 先准备一个userlist.txt,里面加一下用户名称,如下:
- [es@bigdata-senior01 ~]$ cat userlist.txt
- tom
- amy
- xu.dm
- xu.dm.test
- bus
- dba
- 编辑createUsers.sh脚本
-
- #!/bin/bash
- # 用重定向到/dev/nul来屏蔽屏幕输出
- # 脚本后面需要一个文件名参数
- if [ -z $1 ] ; then
- echo "请在脚本后面输入用户文件列表"
- exit
- else
- if [ ! -e $1 ] ; then
- echo "输入的文件不存在,请检查后重新输入"
- exit
- fi
- fi
-
- read -p "请输入用户密码:" password
- users=$(cat $1) #注意等号两边不能有空格,shell语法坑死人
- echo $users
- for uname in $users
- do
- id $uname &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "$uname,用户已经存在!"
- else
- useradd $uname &> /dev/null
- echo "$password" | passwd --stdin $uname &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "$uname,创建成功"
- else
- echo "$uname,创建失败"
- fi
- fi
-
- done
- 执行脚本:
- [es@bigdata-senior01 ~]$ sudo ./createUsers.sh userlist.txt
- 请输入用户密码:123@abc.com
- tom amy xu.dm xu.dm.test bus dba
- tom,创建成功
- amy,创建成功
- xu.dm,用户已经存在!
- xu.dm.test,创建成功
- bus,创建成功
- dba,创建成功
- 用例2:vi checkHosts.sh,检查文件列表里的主机是否在线
-
- #!/bin/bash
- if [ -z $1 ] ; then
- echo "请输入主机列表文件"
- exit
- fi
-
- if [ ! -e $1 ] ; then
- echo "文件不存在,请检查后重新输入"
- exit
- fi
-
- hostlist=$(cat $1)
- for host in $hostlist ; do
- ping -c 3 -i 0.2 -W 3 $host &> /dev/null
- if [ $? -eq 0 ] ; then
- echo "hosthost 在线"
- else
- echo "hosthost 不在线或被屏蔽"
- fi
-
- done
- [es@bigdata-senior01 ~]$ ./checkHosts.sh hosts.txt
- host:192.168.1.2 在线
- host:www.baidu.com 在线
- host:www.google.com 不在线或被屏蔽
- host:www.cnblogs.cn 在线
- host:www.csdn.net 在线
- host:www.qq.com 在线
- 8.3 while语句
-
- 语法:
- while 条件测试语句
- do
- 执行命令
- done
- 用例1:vi outputNum.sh
-
- #!/bin/bash
- #倒序输出数字
- if [ -z $1 ] ; then
- echo "please input a integer number[1-100]"
- exit
- fi
-
- if [ $1 -gt 100 ] || [ $1 -lt 1 ] ; then
- echo "error num,need number between [1-100]"
- exit
- fi
- num=$1
- while [ $num -ge 1 ] ; do
- echo "numnum"
- #num-1有三种写法
- #let "num=$num - 1"
- #num=$(expr $num - 1)
- let num--
- done
- 用例2:
-
- #!/bin/bash
- #测试随机数,需要输入测试次数
- if [ -z $1 ] ; then
- echo "please input test count number"
- exit
- fi
-
- count=$1
- num=1
- while [ $count -gt 0 ]
- do
- echo "第$num次,随机数:$RANDOM"
- let num++
- let count--
- done
- 8.4、case语句
-
- case 变量值 in
- 模式1)
- 命令1;;
- 模式2)
- 命令2;;
- *)
- 其他命令;;
- esac #就是case反过来和if语句类似,我已经不想吐槽这是什么逻辑
- 模式字符串中可以使用通配符
- 如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令列表。
- 各模式字符串应是唯一的,不应重复出现,并且要合理安排它们的出现顺序。
-
-
- 用例1:修改上面的testRandom.sh
-
- #!/bin/bash
- #测试随机数,需要输入测试次数
- if [ -z $1 ] ; then
- echo "please input test count number"
- exit
- fi
-
- case $1 in
- *[a-z]* | *[A-Z]*)
- echo "输入的是字母,需要输入数字"
- exit;;
- *[0-9]*)
- echo "输入的是数字,符合要求";;
- *)
- echo "输入非法字符无法继续"
- exit;;
- esac
-
- count=$1
- num=1
- while [ $count -gt 0 ]
- do
- echo "第$num次,随机数:$RANDOM"
- let num++
- let count--
- done
- 9、break和continue
-
- break:用于立即终止当前循环的执行,break命令可以使用户从循环体中退出来。
- 语法:break[n] ,其中,n表示要跳出几层循环,默认值为1
-
- continue:跳过循环体中在其之后的语句,会返回到本循环层的开头,进行下一次循环。
- –语法:continue[n],其中,n表示从包含continue语句的最内层循环体向外跳到第几层循环,默认值为1,循环层数是由内向外编号。
-
-
- 假设我们定义了一个变量为:
- file=/dir1/dir2/dir3/my.file.txt
-
- 可以用${ }分别替换得到不同的值:
- ${file#*/}:删掉第一个 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
- ${file##*/}:删掉最后一个 / 及其左边的字符串:my.file.txt
- ${file#*.}:删掉第一个 . 及其左边的字符串:file.txt
- ${file##*.}:删掉最后一个 . 及其左边的字符串:txt
- ${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3,#取父件夹路径
- ${file%%/*}:删掉第一个 / 及其右边的字符串:(空值)
- ${file%.*}:删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
- ${file%%.*}:删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
-
- 记忆的方法为:
- # 是 去掉左边(键盘上#在 $ 的左边)
- %是去掉右边(键盘上% 在$ 的右边)
- 单一符号是最小匹配;两个符号是最大匹配
- ${file:0:5}:提取最左边的 5 个字节:/dir1
- ${file:5:5}:提取第 5 个字节右边的连续5个字节:/dir2
- 也可以对变量值里的字符串作替换:
- ${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt
- ${file//dir/path}:将全部dir 替换为 path:/path1/path2/path3/my.file.txt
-
- 利用 ${ } 还可针对不同的变数状态赋值(沒设定、空值、非空值):
- ${file-my.file.txt} :假如 $file 沒有设定,則使用 my.file.txt 作传回值。(空值及非空值時不作处理)
- ${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作处理)
- ${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作处理)
- ${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作处理)
- ${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。(空值及非空值時不作处理)
- ${file:=my.file.txt} :若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為my.file.txt 。 (非空值時不作处理)
- ${file?my.file.txt} :若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
-
- ${file:?my.file.txt} :若 $file 没设定或为空值,则将 my.file.txt 输出至 STDERR。 (非空值時不作处理)
- ${#var} 可计算出变量值的长度:
-
- ${#file} 可得到 27 ,因为/dir1/dir2/dir3/my.file.txt 是27个字节
- ✴️✴️✴️✴️Shell 数组
- bash支持一维数组(不支持多维数组),并且没有限定数组的大小。
-
- 类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。
-
- 定义数组
- 在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
-
- 数组名=(值1 值2 ... 值n)
- 例如:
-
- array_name=(value0 value1 value2 value3)
- 或者
-
- array_name=(
- value0
- value1
- value2
- value3
- )
- 还可以单独定义数组的各个分量:
-
- array_name[0]=value0
- array_name[1]=value1
- array_name[n]=valuen
- 可以不使用连续的下标,而且下标的范围没有限制。
-
- 读取数组
- 读取数组元素值的一般格式是:
-
- ${数组名[下标]}
- 例如:
-
- valuen=${array_name[n]}
- 使用 @ 符号可以获取数组中的所有元素,例如:
-
- echo ${array_name[@]}
- 获取数组的长度
- 获取数组长度的方法与获取字符串长度的方法相同,例如:
-
- # 取得数组元素的个数
- length=${#array_name[@]}
- # 或者
- length=${#array_name
- }
- # 取得数组单个元素的长度
- lengthn=${#array_name[n]}
- Shell 注释
- 以 # 开头的行就是注释,会被解释器忽略。
-
- 通过每一行加一个 # 号设置多行注释,像这样:
-
- #--------------------------------------------
- # 这是一个注释
- # author:菜鸟教程
- # site:www.runoob.com
- # slogan:学的不仅是技术,更是梦想!
- #--------------------------------------------
- ##### 用户配置区 开始 #####
- #
- #
- # 这里可以添加脚本描述信息
- #
- #
- ##### 用户配置区 结束 #####
- 如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?
-
- 每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。
-
- 多行注释
- 多行注释还可以使用以下格式:
-
- :<<EOF
- 注释内容...
- 注释内容...
- 注释内容...
- EOF
- EOF 也可以使用其他符号:
-
- :<<'
- 注释内容...
- 注释内容...
- 注释内容...
- '
-
- :<<!
- 注释内容...
- 注释内容...
- 注释内容...
- !
- ✴️✴️✴️✴️✴️函数,函数必须写在调用的前面
- 实例
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- funWithReturn(){
- echo "这个函数会对输入的两个数字进行相加运算..."
- echo "输入第一个数字: "
- read aNum
- echo "输入第二个数字: "
- read anotherNum
- echo "两个数字分别为 $aNum 和 $anotherNum !"
- return $(($aNum+$anotherNum))
- }
- funWithReturn
- echo "输入的两个数字之和为 $? !"
- 输出类似下面:
-
- 这个函数会对输入的两个数字进行相加运算...
- 输入第一个数字:
- 1
- 输入第二个数字:
- 2
- 两个数字分别为 1 和 2 !
- 输入的两个数字之和为 3 !
- 函数返回值在调用该函数后通过 $? 来获得。
-
- 注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。
-
- 函数参数
- 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...
-
- 带参数的函数示例:
-
- 实例
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- funWithParam(){
- echo "第一个参数为 $1 !"
- echo "第二个参数为 $2 !"
- echo "第十个参数为 $10 !"
- echo "第十个参数为 ${10} !"
- echo "第十一个参数为 ${11} !"
- echo "参数总数有 $# 个!"
- echo "作为一个字符串输出所有参数 $* !"
- }
- funWithParam 1 2 3 4 5 6 7 8 9 34 73
- 输出结果:
-
- 第一个参数为 1 !
- 第二个参数为 2 !
- 第十个参数为 10 !
- 第十个参数为 34 !
- 第十一个参数为 73 !
- 参数总数有 11 个!
- 作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
- 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
-
- 另外,还有几个特殊字符用来处理参数:
-
- 参数处理 说明
- $# 传递到脚本或函数的参数个数
- $* 以一个单字符串显示所有向脚本传递的参数
- $$ 脚本运行的当前进程ID号
- $! 后台运行的最后一个进程的ID号
- $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
- $- 显示Shell使用的当前选项,与set命令功能相同。
- $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
- 
- 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 之间的内容作为输入。
- 需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
-
- 输出重定向
- 重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:
-
- command1 > file1
- 上面这个命令执行command1然后将输出的内容存入file1。
-
- 注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。
-
- 实例
- 执行下面的 who 命令,它将命令的完整的输出重定向在用户文件中(users):
-
- $ who > users
- 执行后,并没有在终端输出信息,这是因为输出已被从默认的标准输出设备(终端)重定向到指定的文件。
-
- 你可以使用 cat 命令查看文件内容:
-
- $ cat users
- _mbsetupuser console Oct 31 17:35
- tianqixin console Oct 31 17:35
- tianqixin ttys000 Dec 1 11:33
- 输出重定向会覆盖文件内容,请看下面的例子:
-
- $ echo "菜鸟教程:www.runoob.com" > users
- $ cat users
- 菜鸟教程:www.runoob.com
- $
- 如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:
-
- $ echo "菜鸟教程:www.runoob.com" >> users
- $ cat users
- 菜鸟教程:www.runoob.com
- 菜鸟教程:www.runoob.com
- $
- 输入重定向
- 和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:
-
- command1 < file1
- 这样,本来需要从键盘获取输入的命令会转移到文件读取内容。
-
- 注意:输出重定向是大于号(>),输入重定向是小于号(<)。
-
- 实例
- 接着以上实例,我们需要统计 users 文件的行数,执行以下命令:
-
- $ wc -l users
- 2 users
- 也可以将输入重定向到 users 文件:
-
- $ wc -l < users
- 2
- 注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。
-
- command1 < infile > outfile
- 同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中。
-
- 重定向深入讲解
- 一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
-
- 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
- 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
- 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
- 默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
-
- 如果希望 stderr 重定向到 file,可以这样写:
-
- $ command 2>file
- 如果希望 stderr 追加到 file 文件末尾,可以这样写:
-
- $ command 2>>file
- 2 表示标准错误文件(stderr)。
-
- 如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:
-
- $ command > file 2>&1
-
- 或者
-
- $ command >> file 2>&1
- 如果希望对 stdin 和 stdout 都重定向,可以这样写:
-
- $ command < file1 >file2
- command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。
-
- Here Document
- Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。
-
- 它的基本的形式如下:
-
- command << delimiter
- document
- delimiter
- 它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。
-
- 注意:
-
- 结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。
- 开始的delimiter前后的空格会被忽略掉。
- 实例
- 在命令行中通过 wc -l 命令计算 Here Document 的行数:
-
- $ wc -l << EOF
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- EOF
- 3 # 输出结果为 3 行
- $
- 我们也可以将 Here Document 用在脚本中,例如:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- cat << EOF
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- EOF
- 执行以上脚本,输出结果:
-
- 欢迎来到
- 菜鸟教程
- www.runoob.com
- /dev/null 文件
- 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
-
- $ command > /dev/null
- /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
-
- 如果希望屏蔽 stdout 和 stderr,可以这样写:
-
- $ command > /dev/null 2>&1
- 注意:0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。
-
- 这里的 2 和 > 之间不可以有空格,2> 是一体的时候才表示错误输出。
- Shell 文件包含
- 和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。
-
- Shell 文件包含的语法格式如下:
-
- . filename # 注意点号(.)和文件名中间有一空格
-
- 或
-
- source filename
- 实例
- 创建两个 shell 脚本文件。
-
- test1.sh 代码如下:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- url="http://www.runoob.com"
- test2.sh 代码如下:
-
- #!/bin/bash
- # author:菜鸟教程
- # url:www.runoob.com
-
- #使用 . 号来引用test1.sh 文件
- . ./test1.sh
-
- # 或者使用以下包含文件代码
- # source ./test1.sh
-
- echo "菜鸟教程官网地址:$url"
- 接下来,我们为 test2.sh 添加可执行权限并执行:
-
- $ chmod +x test2.sh
- $ ./test2.sh
- 菜鸟教程官网地址:http://www.runoob.com
- 注:被包含的文件 test1.sh 不需要可执行权限。
- 默认 bash 里定义的变量是全局的。
- $ a=10; function b() { a=2; }; b; echo $a
- 执行结果为2
- 即:函数b里对a进行修改后,a的值就发生改变。
- 如果不想b对a的操作不影响全局的值,可以将b中的a设为局部变量。
- 如下所示:
- $ a=10; function b() { local a=2; }; b; echo $a
- 执行结果为10
- 还有一点需要注意。
- 在pipe之后的处理不会改变原来的值,因为新建一个进程。
- $ a=1; echo $a; echo "hello, world" | while read line ; do a=2; echo $a; done; echo $a
- 执行结果为:
- 1
- 2
- 1
- 虽然在 pipe 后a改成2,但是最后输出的值仍是最初的1
-
- ho $a; echo "hello, world" | while read line ; do a=2; echo $a; done; echo $a
- 执行结果为:
- 1
- 2
- 1
- 虽然在 pipe 后a改成2,但是最后输出的值仍是最初的1
-
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。