Bash 的变量与数据结构

主要学习资料: bash - GNU Bourne-Again SHell

编程常用操作

用 read 实现多变量赋值

用下面的命令可以同时定义多个变量,并赋值:

read a b c <<< "1 2 3";echo "$a|$b|$c"

参考:Linux bash:多个变量赋值

读取命令行参数

内置命令 getopts

最新代码 getopts.sh

用 getopts 解析命令行参数,getopts 不支持长格式,只能用 -h 这样的短格式:

while getopts "p:h:" option ;do
    if [[ $option == "p" ]];then
        echo "listener port: $OPTARG"
    fi
    if [[ $option == "h" ]];then
        read name value <<< "${OPTARG//:/ }";
        headers="$headers\nproxy_set_header $name $value;"
    fi
done

echo $headers

执行效果如下:

$ ./getopts.sh -p 80 -h h1:v1 -h h2:v2
listener port: 80

proxy_set_header h1 v1;
proxy_set_header h2 v2;

用遍历参数的方式实现

最新代码 loop.sh

#! /bin/sh
#
# loop.sh
# Copyright (C) 2019 lijiaocn <[email protected] wechat:lijiaocn>
#
# Distributed under terms of the GPL license.
#

function help(){
	echo "usage: $0 [-f file] [-n name] [arguments...]"
}

while :; do
	case $1 in
		-h|-\?|--help)
			help
			exit
			;;
		-f|--file)
			if [[ $2 == "" || ${2:0:1} == "-" ]];then
				echo 'ERROR: "--file" requires a non-empty option argument.' 2>&1
				exit 1
			fi
			file=$2
			shift
			;;
		-n|--name)
			if [[ $2 == "" || ${2:0:1} == "-" ]];then
				echo 'ERROR: "--name" requires a non-empty option argument.' 2>&1
				exit 1
			fi
			name=$2
			shift
			;;
		-|--)
			shift
			break
			;;
		*)
			break
			;;
	esac
	shift
done

echo "file is: $file"
echo "name is: $name"
echo "arguments: $*"

一个复杂一些的示例:docker-nginx-tranproxy/entrypoint.sh

变量值读取

Bash 的变量值读取支持很多扩展方式(Parameter Expansion)。

未定义变量

针对变量是否定义,存在以下几种处理方法:

${parameter:-word}:  如果变量没有定义或者 null,返回默认值 word
${parameter:=word}:  如果变量没有定义或者 null,为变量赋值 word,并返回 word
${parameter:?word}:  如果变量没有定义或者 null,在错误输出中显示 word
${parameter:+word}:  如果定义变量,注意是是定义了!用 word 替代(返回 word)

示例:

echo "未定义的变量,使用默认值:${notdef1:-word}  $notdef1"
echo "未定义的变量,赋值默认值:${notdef2:=word}  $notdef2"
echo "已定义的变量,使用替代数值:${notdef2:+valueinstead}"
echo "未定义的变量,打印错误信息:${notdef3:?notdef}"

执行结果为:

未定义的变量,使用默认值:word
未定义的变量,赋值默认值:word  word
已定义的变量,使用替代数值:valueinstead
./variable.sh: line 13: notdef3: notdef

字符串处理

如果变量的值是字符串,在读取时可以直接进行处理。

下面的用法对非字符串变量也可以使用,譬如@*,譬如数组,操作含义也相应变化,变量为字符串时,这些操作的含义是最好理解的:

${parameter:offset}         :第一个字符以后的字符,不包括第一个
${parameter:offset:length}  :第一个字符以后的两个字符,不包括第一个

 :如果变量是 @,显示从 offset 开始的 length 个命令行参数
 :如果变量是 Array[@] 或 Array[*] ,显示从 offset 开始的 length 个数组成员

${#parameter}               :字符串长度

 :如果变量是 @ 或 *,返回命令行参数个数
 :如果变量是 Array[@] 或 Array[*],返回数组的成员数量

${parameter#word}           :从字符串头开始,去掉 word 匹配的部分,最短匹配
${parameter##word}          :从字符串头开始,去掉 word 匹配的部分,最长匹配
${parameter%word}           :从字符串尾开始,去掉 word 匹配的部分,最短匹配
${parameter%%word}          :从字符串尾开始,去掉 word 匹配的部分,最长匹配
${parameter/pattern/string} :字符后换,默认只替换第一个
                             pattern 以 "/" 开头,全替换
                             pattern 以 "#" 开头,从首字母开始匹配
                             pattern 以 "%" 开头,从尾字母开始匹配

 :如果变量是 @ 或 *,对所有命令行参数进行处理,返回处理后的列表
 :如果变量是 Array[@] 或 Array[*],对数组所有成员进行处理,返回处理后的列表

示例:

str="abcddeddfghijka"

echo "第0个字符后: ${str:0}"
echo "第1个字符后: ${str:1}"
echo "第1个字符后的两个字符: ${str:1:2}"

echo "字符串长度:${#str}"

echo "head match #abc: ${str#abc}"
echo "head match ##abc: ${str##abc}"

echo "head match #abc*: ${str#abc*}"
echo "head match ##abc*: ${str##abc*}"

echo "tail match %ijk: ${str%ijk}"
echo "tail match %%ijk: ${str%%ijk}"

echo "tail match %*ijk: ${str%*ijk}"
echo "tail match %%*ijk: ${str%%*ijk}"

echo "replace /d/D(替换一个): ${str/d/D}"
echo "replace //d/D(全部替换): ${str//d/D}"
echo "replace /#a/A(从开始处匹配替换): ${str/#a/A}"
echo "replace /%a/A(从结尾处匹配替换): ${str/%a/A}"

执行结果为:

第0个字符后: abcddeddfghijka
第1个字符后: bcddeddfghijka
第1个字符后的两个字符: bc
字符串长度:15
head match #abc: ddeddfghijka
head match ##abc: ddeddfghijka
head match #abc*: ddeddfghijka
head match ##abc*:
tail match %ijk: abcddeddfghijka
tail match %%ijk: abcddeddfghijka
tail match %*ijk: abcddeddfghijka
tail match %%*ijk: abcddeddfghijka
replace /d/D(替换一个): abcDdeddfghijka
replace //d/D(全部替换): abcDDeDDfghijka
replace /#a/A(从开始处匹配替换): Abcddeddfghijka
replace /%a/A(从结尾处匹配替换): abcddeddfghijkA

读取变量名

注意读取的是变量的名字,不是变量值,

${!prefix*},${!prefix@}: 返回以 prefix 为前缀的已经定义的变量名

示例:

prefixV1="a"
prefixV2="b"
prefixV3="c"

echo ${!prefix*}
echo ${!prefix@}

执行结果为:

prefixV1 prefixV2 prefixV3
prefixV1 prefixV2 prefixV3

读取数组信息

见数组章节。

${!name[@]},${!name[*]}:读取数组的 key 值

数组

使用下面的形式赋予值时,会自动创建数组变量:

array["0"]="value0"
array["1"]="value1"
array["2"]="value2"
array["5"]="value5"

如果要声明一个数组类型的变量,用 declare -a 声明:

# 声明数组变量 array
declare -a array

用下面的方式生成一个带有值数组变量:

array=("value0" "value1" "value2" "value3")

读取数组中指定位置的值:

echo ${array[1]}

读取数组的信息:

# 打印数组中的所有值
for i in ${array[@]}
do
    echo "data: $i"
done

# 打印数组中有值的 index
for i in ${!array[@]}
do
    echo "index: $i"
done

# 打印数组内元素个数
echo "array size: ${#array[@]}"

参考

  1. 李佶澳的博客