Shell脚本语法

    技术2022-07-11  85

    文章目录

    简介shell符号,内置命令速记调试语法命令的排序重定向输出shell通配符echo颜色输出shell变量shell脚本内shell脚本外 在shell脚本中执行其他解释器语言命令替换与引号数值计算变量操作,修改及替换条件测试比较参数文件测试 case循环并发执行数组普通数组关联数组 函数select

    简介

    虽然shell脚本语法不难,难的是各项指令,但是还是记录下常规的命令。

    shell符号,内置命令速记

    符号 () : 在子shell执行命令 (ls) (()):可以进行数值比较 ((1<2)) $()与``:应用替换 touch $(date +%F)_file.txt $(()):数值运算 $((1+2)) ${}:变量的引用 []:条件测试 -a 与条件 -o 或条件 [[ =~ ]]: 正则匹配 &&与条件 ||或条件 $[]:整数运算 $[2**10] 210次方 内置命令 : 返回真与true同效 wait 内部变量,等待前面所有后台进程结束 ######## OLD_IFS=$IFS IFS=$'\n' 内部变量,重新定义分隔符 IFS=$OLD_IFS 恢复之前的换行符 ######## sleep 1 内部关键词,睡眠1s exit 程序退出 echo -n 不换行打印 continue 和c语言类似,不过后面跟数字可以跳过多次循环 break 和C语言类似,不过后面跟数字可以跳出多层循环 shift 将位置参数向左移动,可以跟数字 #!/usr/bin/bash while [ $# -ne 0 ] do let sum+=$1 #将外界的位置参数左移一位,舍去最左边的那个 shift 1 done echo "sum: $sum"

    调试语法

    sh -n xx.sh 仅调试syntax error sh -vx xx.sh 以调试的方式执行,查询整个执行过程 time xx.sh 计算脚本运行的时间

    命令的排序

    #!/usr/bin/bash ping -c1 www.baidu.com && echo "www.baidu.com is up" || echo "www.baidu.com is down"

    && 和 || 的语法其实和C语言差不多,在C语言中&&前一项正确会执行后面一项,如果&&前面一项错误就不会执行后面的,||是前面正确就不执行后面的,否则会执行后面的,所以上面语句可以以||为分界线话为两部分。 ping -c1 www.baidu.com 成功就会执行echo “www.baidu.com is up”,这个必然成功,所以echo "www.baidu.com is down"不会输出。 ping -c1 www.baidu.com 成功则"www.baidu.com is up"不执行,会执行echo “www.baidu.com is down”

    重定向输出

    当文件描述符(0,1,2)与重定向符号(<)组合之后,就可以重新定向输入,输出,及错误。 *

    command 2>file1 命令执行的错误信息保存到了file1文件中。显示屏只是显示正确的信息。 command 1>file1 2>file2 命令执行后,没有显示。因为正确输出到file1,错误定向到file2 command &>file1 命令执行后,输出和错误都定向到file1中 command >&文件描述符 定义“错误”输出到STDERR指定的文件 cat test.sh #!/bin/bash echo " this is ERROR " >&2 错误输出指定到文件file1 [root@localhost ~]# ./test 2>file1 this is output >> :追加重定向输出

    shell通配符

    和正则表达式差不多,但是有差别不能混为一谈

    * : 匹配任意字符 ? : 匹配任意一个字符 []: 匹配括号中的惹你一个字符[abc][a-z][0-9 ][a-zA-Z0-9][^a-z](取反) (): 在子shell中执行命令 {}: 集合 touch file{1..9},创建file1到file9 \ : 转义符

    echo颜色输出

    echo -e :解释特殊符号.如c语言中的printf echo -e "a\tb" # 输出红色字 echo -e "\e[1;33mThis is a red text.\e[0m"

    格式化的输出使用printf指令

    shell变量

    shell脚本内

    显示赋值 :ip = 10.1.1.1 隐式赋值 :read -p "Please input a ip: " ip 执行脚本时等待用户输入ip 引用变量 : 变 量 名 或 变量名或 {变量名} 查看变量 :echo $变量名 预定义变量

    $0 脚本名 $* 所有的参数 $@ 所以的参数 $# 参数格式 $$ 当前进程PID $! 上一个后台进程的PID $? 上一个命令的返回值

    shell脚本外

    当前变量 :ip1=3.3.3.3 只在当前shell生效 环境变量 :export ip2=4.4.4.4 当前shell及其所有子shell 位置变量 : $1,$2 ,执行脚本时后接的参数 ./*.sh arg1 arg2 在脚本中arg1替换$1,arg2替换$2

    在shell脚本中执行其他解释器语言

    #!/usr/bin/bash ls /usr/bin/python <<-EOF print "hello word!" EOF echo "hello bash"

    其中EOF与MySql中的标准分隔号类似(DELIMITER // … //)可以换成任意字符。

    命令替换与引号

    内部命令被shell先执行

    #执行basename $0命令获取脚本文件后输出 echo "name: `basename $0`" 或者 echo "name: $(basename $0)"

    ‘’: 强引用,直接输出内部字符串 “”:进行内部语句分析之后在输出

    name="ljw" boy="$name is good.";echo $boy # 输出 ljw is good. boy='$name is good.';echo $boy # 输出$name is good.

    数值计算

    1. expr expr 1 + 2 expr $num1 + $num2 2. 两个括号 echo $((num1+num2)) echo $((num1-num2)) echo $((num1*num2)) echo $((num1%num2)) echo $((num1/num2)) echo $((2**3)) #23次方 3. $[] echo $[2**3] 4.let let num=1+2

    变量操作,修改及替换

    输出变量的长度 url=www.sina.com.cn echo ${#url} 删除部分值 url=www.sina.com.cn echo ${url#*.} //此次的引用变为sina.com.cn,从前往后开始匹配删除最短匹配项 echo $(url##*.) //此次的引用变为cn,从前往后开始匹配删除最长匹配项 echo ${url%.*} //此次的引用变为www.sina.com,从后往前开始匹配删除最短匹配项 echo ${url%%.*} //此次的引用变为www,从后往前开始匹配删除最长匹配项 切片 echo ${url:0:5} //www.s echo ${url:5:5} //ina.c echo ${url:5:5} //ina.com.cn 替换 echo ${url/sina/baidu} //www.baidu.com.cn echo ${url/n/N} //www.siNa.com.cn echo ${url//n/N} //www.siNa.com.cN

    ${变量名-新变量值} 变量没有被赋值:会使用"新的变量值"替代 变量有被赋值(包括空值):不会被替代

    ${变量名:-新变量值} 变量没有被赋值(包括空值):会使用"新的变量值"替代 变量有被赋值:不会被替代

    ${变量名+新变量值} 变量没有被赋值:不做处理 变量有被赋值(包括空值):使用新变量替代

    ${变量名:+新变量值} 变量没有被赋值(包括空值):不做处理 变量有被赋值:使用新变量替代

    ${变量名=新变量值} 变量没有被赋值:使用新变量作传回值,并将变量名赋值为新值 变量有被赋值(包括空值):不做处理

    ${变量名:=新变量值} 变量没有被赋值(包括空值):使用新变量作传回值,并将变量名赋值为新值 变量有被赋值:不做处理

    ${变量名?新变量值} 变量没有被赋值:将新值输出到STDERR 变量有被赋值(包括空值):不做处理

    ${变量名:?新变量值} 变量没有被赋值(包括空值):将新值输出到STDERR 变量有被赋值:不做处理

    条件测试

    三种比较方法:test ,[] ,[[]]。第三种支持正则表达式的比较

    正则匹配:=~ num2=10 [[ "$num2" =~ ^[0-9]+$ ]];echo $? 0

    比较参数

      -eq 等于   -ne 不等于   -gt 大于   -lt 小于   -le 小于等于   -ge 大于等于   -z 空串   = 两个字符相等   != 两个字符不等   -n 非空串

    文件测试

    判断目录 test -d /home //使用$?返回0 #目录是否存在。不存在创建 #!/usr/bin/bash back_dir=/var/mysql_back if ! test -d $back_dir;then # 等同于 if [ ! -d $back_dir ];then mkdir -p $back_dir fi 数值比较 #/usr/bin/bash # 用户不为root用户 if [ $UID -ne 0 ];then exit fi yum -y install httpd 字符串比较

    字符串比较尽量使用双引号

    #/usr/bin/bash # 用户不为root用户 if [ "$USER" != "root" ];then exit fi yum -y install httpd

    其他的对比选项可以通过 man test查看

    case

    case "$stringvalue" in yes|y|Y|YES) # do something ;; "no") # do otherthing ;; *) # do anything esac

    循环

    for循环 #产生序列1-10 for i in `seq 1 10` #显示遍历 for i in a b c d e #文件遍历 for i in `ip.txt` for 变量名 in {1..10} do #做某一些事情 done # 单纯的for in 等价于 for in $@ for in do done while #死循环 while true do ... done # while从文件里面读取用户名创建账户 while read user do id $user &>/dev/null if [ $? -eq 0 ];then echo "user $user already exists" else useradd $user if [ $? -eq 0 ];then echo "$user is created." fi fi done < user.txt # while固定循环次数 i=2 while [ $i -le 254 ] do #做一些事 let i++ done

    for与while的区别:for是以空格为分隔符,在读取文件时如果按行读取的话比较麻烦要自己定义分隔符,而while是按行读取,但是空行对while有影响对for无影响

    until # until与while相反,条件成立时推出循环 # 当能ping通主机时退出 ******************************** #!/usr/bin/bash ip=192.168.1.100 until ping -c1 -W1 $ip &>/dev/null do sleep 1 done echo "$ip is up."

    并发执行

    通过后台执行实现并发 {}& #!/usr/bin/bash for i in {1..254} do { ip=192.168.1.$i ping -c1 -W1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up" fi }& #放后台并发执行 done wait 控制并发数量 #!/usr/bin/bash process=5 #以当前进程pid生成一个命名管道 tmp_fifofile=/tmp/$$.fifo mkfifo $tmp_fifofile #以文件描述符8打开命名管道,并删除文件,并不影文件描述符的使用 exec 8<> $tmp_fifofile rm $tmp_fifofile # 给管道输入5个回车 for `seq $process` do echo >&8 done for i in {1..254} do #从文件描述符里面读取东西 read -u 8 { ip=192.168.1.$i ping -c1 -W1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up" else echo "$ip is down" fi #退还回车 echo >&8 }& done wait

    数组

    普通数组

    book=(linux shell awk openstack docker) 普通数组 ----------------------------------- |linux|shell|awk|oopenstack|docker| ----------------------------------- | 0 | 1 | 2 | 3 | 4 |索引(下标) # 引用 ${books[4]} docker ${books[@]} 数组中所有的值 ${#book[@]} 数组元素的个数 ${!books[@]} 数组所有的索引 定义数组 #方法一:一次赋一个值 数组名[下标]=变量值 #array1[0]=pear #array1[1]=apple #array1[2]=orange #array1[3]=peach #方法二:一次赋多个值 #array2=(tom jack alice) #array3=(`cat /etc/passwd`) 将该文件中的每一个行作为一个元素赋值给数组array3(注意空格问题) #array4=(`ls /var/ftp/Shell/for*`) #array5=(tom jack alice "bash shell") 四个颜色 #colors=($red $blue $green $recolor) #array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack) # 查看目前变量中的所有数组 declare -a 数组切片 echo ${book[@]:2} # awk openstack docker echo ${book[@]:2:2} # awk openstack

    关联数组

    # 声明一个关联数组 declare -A info info=([name]=tianyun [sex]=male [age]=36 [height]=170 [skill]=cloud)关联数组 -------------------------------- |ljw|male |25 |170 |cloud | -------------------------------- | name | sex |age|height|skill| 索引 #添加一个新的元素 info+=([x]=1) # 引用 echo ${info[age]} 26

    函数

    定义函数 #方法一: 函数名(){ 代码块 } function 函数名{ 代码块 } 函数参数 #!/bin/bash factorial(){ #定义一个本地变量 local factorial=1 #定义一个位置变量,该位置变量不是外界的位置变量,而是调用函数时的位>置变量 for i in `seq $1` do let factorial*=$i done #调用外部变量 echo "name is $name" echo "$1 的阶乘是: $factorial" } # 外部变量 name="lujw" # 以外界输入的位置变量作为程序的位置变量 factorial $1 函数的返回值

    以上面函数为例

    factorial 5 # 返回的是函数**最后一条语句**执行的结果 echo "factorial return value: $?"

    在函数内部的返回return,但是最高只能到255。超过255不支持

    正常返回值 #!/bin/bash fun2(){ read -p "enter num: " num echo $[2*$num] } #获取函数返回值 result=`fun2` 数组传参 #!/bin/bash num=(1 2 3 4 5) array(){ factorial=1 for i in "$@" do let factorial*=$i done echo "$factorial" } array ${num[*]} #数组所有元素值 返回数组 #!/usr/bin/bash num=(1 2 3) array(){ #echo "all parameters: $*" local newarray=($*) local i for((i=0;i<$#;i++)) do newarray[$i]=$(( ${newarray[$i]} * 5 )) done echo "$newarray[*]" } # 传出数组 # 在子shell中调用函数,函数中的全局变量在此无法显示 result=`array ${num[*]}` echo ${result[*]}

    总结

    函数接收位置参数 $1 $2 $3 … $n函数接收数组变量 $* 或 $@函数将接收到的所有参数赋值给数组newarray=($*)

    select

    在shell脚本中select可以创建一个选择列表,一般与case连用 示例如下:

    #!/usr/bin/bash # 将输入提示符提示符替换成">>",默认为#? PS3=">>" select choice in disk_partition filesystem cpu_load mem_util quit do case "$choice" in disk_partition) fdisk -l ;; filesystem) df -h ;; cpu_load) uptime ;; mem_util) free -m ;; quit) break ;; *) echo "error" exit esac done
    Processed: 0.012, SQL: 12