您的位置:首页 > 运维架构 > Shell

Shell编程入门总结(三)

2016-04-30 13:56 609 查看
本文的主要内容:

1、数组

2、命令组合

3、进程替换

4、陷阱机制

5、常见错误与防错

一、数组

Bash 中的数组仅限制为单一维度。对数组的支持第一次出现在 bash 版本2中。

1.创建一个数组

$ a[1]=foo

$ echo ${a[1]}

foo

其中第一个命令把数组 a 的元素1赋值为 “foo”,第二个命令显示存储在元素1中的值。在第二个命令中使用花括号是必需的, 以便防止 shell 试图对数组元素名执行路径名展开操作

也可以用 declare 命令创建一个数组:

$ declare -a a
使用 -a 选项,declare 命令的这个例子创建了数组 a

2.数组赋值

有两种方式可以给数组赋值。

单个值赋值使用以下语法:name[subscript]=value

多个值赋值使用下面的语法:name=(value1 value2 ...)

例如:

$ days=(Sun Mon Tue Wed Thu Fri Sat)

$ days=([0]=Sun [1]=Mon [2]=Tue [3]=Wed [4]=Thu [5]=Fri [6]=Sat)

3.输出整个数组的内容

下标 * 和 @ 可以被用来访问数组中的每一个元素
例如:

$ animals=("a dog" "a cat" "a fish")

$ for i in ${animals[*]}; do echo $i; done

a

dog

a

cat

a

fish

$ for i in ${animals[@]}; do echo $i; done

a

dog

a

cat

a

fish

$ for i in "${animals[*]}"; do echo $i; done

a dog a cat a fish

$ for i in "${animals[@]}"; do echo $i; done

a dog

a cat

a fish

4.确定数组元素个数

例如:

$ a[100]=foo

$ echo ${#a[@]} #数组元素个数

1

$ echo ${#a[100]} #第100元素的长度

3

尽管我们把字符串赋值给数组元素100,但bash仅仅报告数组中有一个元素,这不同于一些其它语言

5.找到数组使用的下标

因为 bash 允许赋值的数组下标包含“间隔”,确定哪个元素真正存在可以使用以下形式的参数展开:

${!array[*]}

${!array[@]}

例如:

$ foo=([2]=a [4]=b [6]=c)

$ for i in "${foo[@]}"; do echo $i; done

a

b

c

$ for i in "${!foo[@]}"; do echo $i; done

2

4

6

6.在数组末尾添加元素

使用 += 赋值运算符
例如:

$ foo=(a b c)

$ echo ${foo[@]}

a b c

$ foo+=(d e f)

$ echo ${foo[@]}

a b c d e f

7.数组排序

Shell 没有直接排序的方法,但可通过管道给sort实现
例如:

#!/bin/bash

# array-sort : Sort an array

a=(f e d c b a)

echo "Original array: ${a[@]}"

a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort))

echo "Sorted array: ${a_sorted[@]}"

8.删除数组

删除一个数组,使用 unset 命令:

$ foo=(a b c d e f)

$ unset foo

亦可用unset命令删除单个的数组元素:

$ foo=(a b c d e f)

$ unset 'foo[2]'

注:数组下标开始于0,数组元素用引号引起来可防止 shell执行路径名展开操作。

给一个数组赋空值不会清空数组内容:

$ foo=(a b c d e f)

$ foo=

$ echo ${foo[@]}

b c d e f

引用一个不带下标的数组变量,则指的是数组元素0:

$ foo=(a b c d e f)

$ echo ${foo[@]}

a b c d e f

$ foo=A

$ echo ${foo[@]}

A b c d e f

9.关联数组

例如创建颜色数组

declare -A colors

colors["red"]="#ff0000"

colors["green"]="#00ff00"

colors["blue"]="#0000ff"

访问关联数组元素:

echo ${colors["blue"]}

二、命令组合

用一个 group 命令,或者用一个子 shell可以把命令组合在一起

组命令:{ command1; command2; [command3; ...] }
注:花括号与命令之间必须有一个空格,并且最后一个命令必须用一个分号或者一个换行符终止

子 shell:(command1; command2; [command3;...])

例如:

ls -l > output.txt
echo "Listing of foo.txt" >> output.txt
cat foo.txt >> output.txt


可以用命令组合改为:

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt



(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt


三、进程替换

1.当子 shell 退出时,环境副本会消失,所以在子 shell 环境(包括变量赋值)中的任何更改也会消失,例如:

echo "foo" | read

echo $REPLY
因为管道线中的命令总是在子 shell 中执行,所以该REPLY变量的内容总是为空

2.进程替换有两种表达方式:

一种适用于产生标准输出的进程:<(list)

另一种适用于接受标准输入的进程:>(list)

其中list 是一串命令列表

3.解决上面的 read 命令问题,可用进程替换,例如:

read < <(echo "foo")

echo $REPLY

4.进程替换允许我们把一个子 shell 的输出结果当作一个用于重定向的普通文件,检验如下:

$ echo <(echo "foo")

/dev/fd/63
使用 echo 命令,查看展开结果,我们看到子 shell 的输出结果,由一个名为 /dev/fd/63 的文件提供

5.例子:

#!/bin/bash
# pro-sub : demo of process substitution
while read attr links owner group size date time

filename; do
cat <<- EOF
Filename:     $filename
Size:         $size
Owner:        $owner
Group:        $group
Modified:     $date $time
Links:        $links
Attributes:   $attr
EOF
done < <(ls -l | tail -n +2)


这个循环对目录列表的每一个条目执行 read 命令。列表本身产生于该脚本的最后一行代码。这一行代码把从进程替换得到的输出 重定向到这个循环的标准输入。这个包含在管道线中的
tail 命令,是为了消除列表的第一行文本,这行文本是多余的。

四、陷阱(trap)机制

1.trap语法如下

trap argument signal [signal...]

其中argument是一个字符串,它被读取并作为一个命令,signal 是一个信号的说明,它会触发执行所要解释的命令

2.例子:

#!/bin/bash
# trap-demo : simple signal handling demo
trap "echo 'I am ignoring you.'" SIGINT SIGTERM
for i in {1..5}; do
echo "Iteration $i of 5"
sleep 5
done


此脚本定义一个陷阱,当脚本运行时每当接受到一个 SIGINT 或 SIGTERM 信号时,就会执行一个 echo 命令

五、常见错误与防错

1.语法错误

丢失引号:

......

if [ $number = 1 ]; then

echo "Number is equal to 1.

else

......

其中echo语句后面缺少一个引号

丢失或意外的标记:

......

if [ $number = 1 ] then

echo "Number is equal to 1."

else

......

其中if测试语句后面缺少一个分号

预料不到的展开

......

number=

if [ $number = 1 ]; then

......

其中当number展开后为[ = 1 ],若为测试语句的第一个参数添加双引号[ "$number" = 1 ]可更正此问题,此时展开后为[ "" = 1 ]

2.防错编程:验证假设

例如:

cd $dir_name
rm *

若dir_name所指的目录不存在则会删除当前工作目录中的所有文件

对此可将命令改为:

[[ -d $dir_name ]] && cd $dir_name && rm *


通常,当某种情况(比如上述问题)发生的时候,最好是终止脚本执行,并对这种情况提示错误信息:

if [[ -d $dir_name ]]; then
if cd $dir_name; then
rm *
else
echo "cannot cd to '$dir_name'" >&2
exit 1
fi
else
echo "no such directory: '$dir_name'" >&2
exit 1
fi


3.其他防错技巧

验证输入

如:[[ $REPLY =~ ^[0-3]$ ]]

测试命令

如:echo rm *

将 rm 命令及其展开的参数列表打印出来,而不是执行实际的rm命令语句

使用echo输出变量内容

。。。

参考:《The Linux Command Line》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: