shell脚本

目录

shell基础

高级特性(if中使用)

结构化命令

①、if-then

②、条件测试

文件比较

变量判空比较

③、case

④、select图形化

⑤、for

⑥、while

⑦、break

特殊变量

数组

 向函数传递数组

函数返回数组

函数

用户输入

位置参数

特殊参数

移动参数

处理参数选项

case

getopt

getopts

获取用户输入

信号


shell基础

#!/bin/bash

### 多命令
command 1; command 2;

### 单命令
command 1

### 转义
\$
\*

### 变量
# 变量的值中含有空格的一律用引号引起来,因为空格会被当做分割符
var1=hello
var2="hello world"
var3=1
# 可以为了避免变量中的特殊字符,在引用变量时全加引号
echo \$var1: $var1
echo \$var2: $var2
echo \$var3: "$var3"

### 执行命令
`ls -la`
$(ls -la)

### 管道(将前一个命令的输出作为第二个命令的输入)
command1 | command2

### 数学运算(非if高级特性)
# expr :缺点:一些运算符会被特殊处理,在shell命令中*会被当做通配符,因此要转义,只支持整数
expr a \* b
# $[] : 方括号内的数学符号不会被误解成其他符号,只支持整数
var2=$[$var1 * 2]
# bc : 支持浮点数
var1=$(echo " scale=4; 3.44 / 5" | bc)

### 退出
# n为退出码0~255(256取余)
exit n

高级特性(if中使用)

        bash shell 还提供了 3 个可在 if-then 语句中使用的高级特性。

### 命令
# 创建子shell执行(command)。
# 如果命令成功结束,则退出状态码会被设为 0,then 部分的命令就会被执行。
if (command)
then
    command
fi

### 数学运算
# 比test和[]多了一些运算符
# 双括号数学运算也可以单独使用,不跟if使用
if(( expression ))
then
fi

(( expression ))

val1=10

#
if (( $val1 ** 2 > 90 ))
then
	(( val2 = $val1 ** 2 ))
	echo "The square of $val1 is $val2,"
	echo ""
#
fi

### 字符串比较
# 相比test和[]多了通配符
if[[ expression ]]
then
fi

if [[ $BASH_VERSION == 5.* ]]
then
	echo ""
fi

if [[ "$var" == "hello world" ]]
then
	echo ""
fi

结构化命令

①、if-then

### if-then
if command
then
	commands
fi

# 等价于
if command; then
	commands
fi

### if-else
if command
then
	commands
else
	commands
fi

### if-elif
if command1
then
	commands
elif command2
then
	more commands
fi

②、条件测试

        使用 test 和 [ ] 可以在if中作为条件测试,用来测试条件是否成立。

        可以测试:数值比较、字符串比较、文件比较。

### test命令
if test condition
then
	commands
fi


### []
if [ condition ]
then
	commands
fi

# 两者等价,通常直接使用[ condition ],注意两边的空格

# 数值比较通常直接使用高级特性 if (( expression ))
if [ $value1 -gt 5 ]
then
	echo "The test value $value1 is greater than 5."
fi

# 字符串比较通常直接使用高级特性 if [[ expression ]]
if [ "$str1" = "helllo world" ]
then
	echo "The test value "$str1" is helllo world."
fi

# 文件比较
if [ -d file ]

# 变量空检测
if [ -n $var ]
if [ -z $var ]

文件比较

        举例:

        脚本会使用-e 测试检查$HOME 是否存在。如果存在,就接着用-f 测试检查其是否为文件。如果不是文件(当然不会是文件),则显示消息,说明这是目录。

        -f 只能检查指定的普通文件是否存在。

        -e 可以检查任何类型的文件(普通文件、目录、符号链接)是否存在。

#!/bin/bash
# Check if object exists and is a directory or a file
#
object_name=$HOME
echo
echo "The object being checked: $object_name"
echo
#
if [ -e $object_name ]
then
	echo "The object, $object_name, does exist,"
#
if [ -f $object_name ]
then
	echo "and $object_name is a file."
#
else
	echo "and $object_name is a directory."
fi
#
else
	echo "The object, $object_name, does NOT exist."
fi

变量判空比较

-n 和-z 可以很方便地用于检查一个变量是否为空

-n 判断变量是否不为空,不为空则执行 then。

-z 判断变量是否为空,为空则执行 then。

(变量中有空格可以直接用引号引起来"")

#!/bin/bash
# Testing string length
#
string1=Soccer
string2=''
#
if [ -n $string1 ]
then
	echo "The string '$string1' is NOT empty"
else
	echo "The string '$string1' IS empty"
fi
#
if [ -z $string2 ]
then
	echo "The string '$string2' IS empty"
else
	echo "The string '$string2' is NOT empty"
fi
#
if [ -z $string3 ]
then
	echo "The string '$string3' IS empty"
else
	echo "The string '$string3' is NOT empty"
fi

③、case

case variable in
pattern1 | pattern2) 
    commands1
    ;;
pattern3) 
    commands2
    ;;
*) 
    default commands
    ;;
esac

while true; do
        select RESULT in "${options[@]}"; do
            case "$RESULT" in
            "BSP" )
                echo "### 开始更新 BSP 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "PLATFORM" )
                echo "### 开始更新 平台 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "DRIVER" )
                echo "### 开始更新 驱动 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "no_update" )
                echo "### 没有需要更新的组件"
                break 2
                ;;
            *)
                echo "### 无效的选择,请重新选择。"
                ;;
            esac
        done
    done

④、select图形化

        list 参数是由空格分隔的菜单项列表,该列表构成了整个菜单,list 中的菜单项必须与 select 命令在同一行(~CA 可以使用续行符)。

        select 命令会将每个列表项显示成一个带编号的菜单项,然后显示一个由 PS3 环境变量定义的特殊提示符,指示用户做出选择。

select variable in list
do
	commands
done
# 提示符,环境变量
PS3="Enter option: "

# ops="Display logged on users" "Display memory usage" "Exit program"

# select命令 ~CA续行符
select option in "Display disk space" "Display logged on users" ~CA
"Display memory usage" "Exit program"
# 等价select option in "$ops"
do
	case $option in
	"Exit program")
		break ;;
	"Display disk space")
		diskspace ;;
	"Display logged on users")
		whoseon ;;
	"Display memory usage")
		memusage ;;
	*)
		clear
		echo "Sorry, wrong selection";;
	esac
done
clear

⑤、for

# shell for
for var in list
do
	commands
done

#!/bin/bash
# using a variable to hold the list

list="Alabama Alaska Arizona Arkansas Colorado"
list=$list" Connecticut"

for state in $list
do
	echo "Have you ever visited $state?"
done


# c for
for (( variable assignment ; condition ; iteration process ))
do
	command
done

#!/bin/bash
# testing the C-style for loop

for (( i=1; i <= 10; i++ ))
do
	echo "The next number is $i"
done

⑥、while

while test command
do
	other commands
done

#!/bin/bash
# testing a multicommand while loop
var1=10
while echo $var1					# 第一条测试命令显示var1当前值
			[ $var1 -ge 0 ]			# 第二条测试命令,判断是否大于0
do
	echo "This is inside the loop"
	var1=$[ $var1 - 1 ]
done

⑦、break

# 跳出单个循环
break

# 嵌套循环,跳出内部循环
break

# 跳出外层循环,n 指定了要跳出的循环层级。
break n

特殊变量

# 命令行参数的个数
$#

# 最后一个命令行参数的值
${!#}

# 将所有的命令行参数视为一个单词。
$*

# 将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数
$@

# 返回上个命令的返回值
$?

数组

        数组是能够存储多个值的变量。这些值既可以单独引用(array[n]),也可以作为整体引用(array[*])。

        环境变量可以当做数组变量使用。

        ${options[@]} : 数组每个元素作为独立的参数传递。

        ${options[*] : 数组每个元素作为一个单独的字符串处理,元素之间用空格分隔。如果数组元素中有空格时,会导致问题。

$ mytest=(zero one two three four)
$
$ echo $mytest
zero
$ echo ${mytest[*]}
zero one two three four

$ echo ${mytest[@]}
zero one two three four

$ echo ${mytest[2]}
two
$
$ mytest[2]=seven
$ echo ${mytest[2]}
seven
$
$ unset mytest[2]
$ echo ${mytest[*]}
zero one three four
$
$ echo ${mytest[2]}
$ echo ${mytest[3]}
three
$
# 定义平台选项
options=("BSP" "PLATFORM" "DRIVER" "no_update")

while true; do
        select RESULT in "${options[@]}"; do
            case "$RESULT" in
            "BSP" )
                echo "### 开始更新 BSP 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "PLATFORM" )
                echo "### 开始更新 平台 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "DRIVER" )
                echo "### 开始更新 驱动 组件..."
                download_component "$RESULT" 1
                break 2
                ;;
            "no_update" )
                echo "### 没有需要更新的组件"
                break 2
                ;;
            *)
                echo "### 无效的选择,请重新选择。"
                ;;
            esac
        done
    done

 向函数传递数组

        数组变量作为参数传入函数时,需要先将数组变量拆解成整体引用(${array[*]}),然后将这些数组元素作为函数参数传递。最后在函数内部,将所有的参数重新组合成一个新的数组变量(array=(`echo "$@"`))。

        $@表示会将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数。

#!/bin/bash
# adding values in an array
function addarray {
	local sum=0
	local newarray
	newarray=(`echo "$@"`)
	for value in ${newarray[*]}
	do
		sum=$[ $sum + $value ]
	done
	echo $sum
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "The result is $result"

函数返回数组

函数向 shell 脚本返回数组变量也采用类似的方法。函数先用 echo 语句按正确顺序输出数组的各个元素,然后脚本再将数组元素重组成一个新的数组变量。 

#!/bin/bash
# returning an array value
function arraydblr {
	local origarray
	local newarray
	local elements
	local i
	origarray=($(echo "$@"))
	newarray=($(echo "$@"))
	elements=$[ $# - 1 ]
	for (( i = 0; i <= $elements; i++ ))
	{
		newarray[$i]=$[ ${origarray[$i]} * 2 ]
	}
	echo ${newarray[*]}
}

myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
arg1=$(echo ${myarray[*]})
result=($(arraydblr $arg1))
echo "The new array is: ${result[*]}"

函数

用户输入

位置参数

        位置变量的名称都是标准数字: $0 对应脚本名, $1 对应第一个命令行参数, $2 对应第二个命令行参数,以此类推,直到$9。

        注意:参数之间是以空格分隔的,所以 shell 会将字符串包含的空格视为两个参数的分隔符。要想在参数值中加入空格,必须使用引号(单引号或双引号均可)

# shell 脚本名
$0
# 第1个命令行参数
$1
···
# 第9个命令行参数
$9

# 避免特殊符号分割,尽量都使用""
"$1" ··· "$9"

# 读取脚本名:去掉路径
name=$(basename $0)

        在使用位置变量之前一定要检查是否为空

if [ -n "$1" ]
then
	command
fi

特殊参数

# 命令行参数的个数
$#

# 最后一个命令行参数的值
${!#}

# 将所有的命令行参数视为一个单词。
$*

# 将所有的命令行参数视为同一字符串中的多个独立的单词,以便你能遍历并处理全部参数
$@

移动参数

        shift 命令会根据命令行参数的相对位置进行移动。默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量$3 的值会移入$2,变量$2 的值会移入$1,而变量$1 的值则会被删除(注意,变量$0 的值,也就是脚本名,不会改变)。

# 默认所有位置变量全部向左移1个,$0不变,原$1的值丢弃
shift

# 同上,向左移n个
shift n

        默认的 shift 命令,是遍历命令行参数的另一种好方法,尤其是在不知道到底有多少参数的时候。你可以只操作第一个位置变量,移动参数,然后继续处理该变量。

#!/bin/bash
# Shifting through the parameters
#
echo
echo "Using the shift method:"
count=1
while [ -n "$1" ]
do
	echo "Parameter #$count = $1"
	count=$[ $count + 1 ]
	shift
done
echo
exit

处理参数选项

case

        处理含值的选项。在这个例子中, case 语句定义了 3 个要处理的选项。 -b 选项还需要一个额外的参数值。由于要处理的选项位于$1,因此额外的参数值就应该位于$2(因为所有的参数在处理完之后都会被移出)。只要将参数值从$2 变量中提取出来就可以了。当然,因为这个选项占用了两个位置,所以还需要使用 shift 命令多移动一次。

        缺点就是不能处理合并的选项,如“-ab”。

#!/bin/bash
# Extract command-line options and values
#
echo
while [ -n "$1" ]
do
	case "$1" in
		-a) echo "Found the -a option" ;;
		-b) param=$2
				echo "Found the -b option with parameter value $param"
				shift;;
		-c) echo "Found the -c option" ;;
		--) shift
				break;;
		*) echo "$1 is not an option" ;;
	esac
	shift
done
#
echo
count=1
for param in $@
do
	echo "Parameter #$count: $param"
	count=$[ $count + 1 ]
done
exit

getopt

        getopt命令用来分解命令行选项和参数。

        命令展示如下:

$ getopt ab:cd -a -b BValue -cd test1 test2

         输出

-a -b BValue -c -d -- test1 test2

         自动将-cd 分成两个单独的选项,并插入双连字符来分隔命令行中额外的参数。

        注意:

        在 shell 脚本里使用 set -- 命令可以自动将命令行参数作为 getopt 的输入

# -q 表示有选项未在optstring中声明时出现的错误则忽略。
set -- $(getopt -q ab:cd "$@")
#!/bin/bash
# Extract command-line options and values with getopt
#
set -- $(getopt -q ab:cd "$@")
#
echo
while [ -n "$1" ]
do
	case "$1" in
		-a) echo "Found the -a option" ;;
		-b) param=$2
				echo "Found the -b option with parameter value $param"
				shift;;
		-c) echo "Found the -c option" ;;
		--) shift
				break;;
		*) echo "$1 is not an option" ;;
	esac
	shift
done
#
echo
count=1
for param in $@
do
	echo "Parameter #$count: $param"
	count=$[ $count + 1 ]
done
exit

getopts

        getopt就是将所有命令行参数处理(结合选项拆分,分隔额外参数)成一个总的输出,通过不断的 shift 取$1、$2 得到选项和值。getopt 是外部的二进制程序。

        而getopts 是 shell 内置命令,getopts 每次只处理一个命令行参数(无论-a、-ab、value)。在处理完所有的参数后, getopts 会退出并返回一个大于 0 的退出状态码(可以很好的用在循环)

getopts optstring variable

### optstring 中指定参数选项。
# 选项如果有参数值,就在其后加一个冒号。如 a:
# 选项如果不想显示错误消息,就在其前加一个冒号。如 :a

### variable 存放正在处理的选项参数。

        getopts 命令要用到两个环境变量。

        OPTARG 环境变量:保存选项后加带的参数值。optstring 中未定义的选项字母会以问号形式传给OPTARG。

        OPTIND 环境变量:保存正在处理的参数位置。初始值为 1, 每处理完一个参数, OPTIND 环境变量值就增 1,得到下一个要处理的参数。

        

#!/bin/bash
# Extract command-line options and parameters with getopts

#
echo
while getopts :ab:cd opt
do
	case "$opt" in
		a) echo "Found the -a option" ;;
		b) echo "Found the -b option with parameter value $OPTARG";;
		c) echo "Found the -c option" ;;
		d) echo "Found the -d option" ;;
		*) echo "Unknown option: $opt" ;;
	esac
done

# 获取处理完剩余的多余参数
shift $[ $OPTIND - 1 ] # OPTIND始终指向下一个要处理的命令行参数,shift左移已经处理过的命令行参数即可。
#
echo
count=1
# 由于前面的参数都被移除了,所以此时$@为剩余的未处理的多余参数
for param in "$@"
do
	echo "Parameter $count: $param"
	count=$[ $count + 1 ]
done
exit

获取用户输入

# read命令用于读取用户输入
read            # 不指定存储的变量,默认存在 $REPLY 环境变量
read var
read -p "message" var1 var2
read -s -p "message" var1 var2  # 不回显用户输入的内容
read -t 5 var    # 等待5s超时,返回非0状态码
read -n 1        # 指定read接收字符个数,此处指定read只接收一个字符就退出


#!/bin/bash
# Using the read command
#
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my script."
exit

# 等价于
#!/bin/bash
# Using the read command
#
read -p "Enter your name: " name
echo "Hello $name, welcome to my script."
exit

# 等价于
#!/bin/bash
# Using the read command
#
read -p "Enter your name: "
echo "Hello $REPLY, welcome to my script."
exit

quitanswer=""
read -t 10 -n 1 -p "Quit script [Y/n]? " quitanswer
#
case $quitanswer in
Y | y) echo
    echo "Quitting script..."
    exit;;
N | n) echo
    echo "Please answer question: "
    choice=0;;
*) echo
    echo "No response. Quitting script..."
    exit;;
esac

信号

        在默认情况下, bash shell 会忽略收到的任何 SIGQUIT(3)信号和 SIGTERM(15)信号(因此交互式 shell 才不会被意外终止)。但是, bash shell 会处理收到的所有 SIGHUP(1)信号和SIGINT(2)信号。

        如果收到了 SIGHUP 信号(比如在离开交互式 shell 时), bash shell 就会退出。但在退出之前,它会将 SIGHUP 信号传给所有由该 shell 启动的进程,包括正在运行的 shell 脚本。

        如果收到 SIGINT 信号, shell 会被中断。 Linux 内核将不再为 shell 分配 CPU 处理时间。当出现这种情况时, shell 会将 SIGINT 信号传给由其启动的所有进程,以此告知出现的状况。

 

# 捕获信号
trap commands/func signals

trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT
trap func SIGINT


# 忽略信号
trap "" signals

trap "" SIGINT

# 捕获shell脚本退出信号EXIT
trap command EXIT


trap "echo Goodbye..." EXIT

# 恢复默认行为
trap -- signal
trap - signal

trap -- SIGINT

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值