shell脚本编程之更多结构化命令_干货

1.for命令

bash shell提供了for命令,允许你创建一个遍历一系列值的循环。每次迭代都使用其中的一个值来执行已定义好的一组命令,for命令的基本格式如下所示:
1for var in list
2do 
3    命令...
4done
在list参数中,需要提供迭代需要的一系列值。可以通过几种不同的方法指定列表中的值。do和done语句中之间输入的命令可以是一条或者多条标准的bash shell命令。也可以将do语句和for语句放在同一行,但是必须用分号将其同列表中的值分开:for var in list; do
1.1 读取列表中的值
for命令最基本的用法就是遍历for命令自身定义的一系列值,如下例所示:
 1#!/bin/bash
 2
 3for li in A B C D E F G
 4do
 5    echo The next state is $li
 6done
 7
 8# 结果
 9[njust@njust tutorials]$ ./dana.sh
10The next state is A
11The next state is B
12The next state is C
13The next state is D
14The next state is E
15The next state is F
16The next state is G
1.2 读取列表中的复杂值

当shell看到list列表中的单引号并尝试使用它们来定义一个单独的数据值时,结果会一团糟。如下例所示:

 1#!/bin/bash
 2
 3for var in I don't know if this'll work
 4do 
 5    echo "word:$var"
 6done
 7
 8# 结果
 9word:I
10word:dont know if thisll
11word:work
两种解决方法:

a.使用转义字符来将单引号进行转义;

b.使用双引号来定义用到单引号的值;

 1#!/bin/bash
 2
 3for var in I don\'t know if "this'll" work
 4do 
 5    echo "word:$var"
 6done
 7
 8# 结果
 9[njust@njust tutorials]$ ./dana1.sh 
10word:I
11word:don't
12word:know
13word:if
14word:this'll
15word:work

注意:for循环假定每个值都是用空格分割的,如果有包含空格的数据值,就会陷入麻烦。如下例所示:

 1#!/bin/bash
 2
 3for var in New York New Mexico North Carolina
 4do
 5    echo "Now going to $var"
 6done
 7
 8# 结果
 9[njust@njust tutorials]$ ./dana2.sh 
10Now going to New
11Now going to York
12Now going to New
13Now going to Mexico
14Now going to North
15Now going to Carolina

for命令用空格来划分列表中的每个值,如果在单独的数据值中有空格,就必须使用双引号将这些值圈起来,在某个值两边使用双引号时,shell并不会将双引号当成值的一部分。如下例所示:

 1#!/bin/bash
 2
 3for var in "New York" "New Mexico" "North Carolina"
 4do
 5    echo "Now going to $var"
 6done
 7
 8# 结果
 9[njust@njust tutorials]$ ./dana2.sh 
10Now going to New York
11Now going to New Mexico
12Now going to North Carolina
1.3 从变量读取列表list

通常情况下,是将一系列值都集中存储在一个变量,然后需要遍历变量中的整个列表,如下例所示:

 1#!/bin/bash
 2
 3list="Red Green Blue"
 4
 5list=$list" Blank"  # 使用赋值语句向$list变量包含的已有列表中添加一个值Blank,这是向变量中存储的已有文本字符串尾部添加文本的常用方法
 6for var in $list
 7do
 8    echo "The current color is $var"
 9done
10
11# 结果
12[njust@njust tutorials]$ ./dana3.sh 
13The current color is Red
14The current color is Green
15The current color is Blue
16The current color is Blank
1.4 从命令中读取值

生成列表中所需值的另一途径就是使用命令的输出。可以使用命令替换来执行任何能产生输出的命令,然后在for命令中使用该命令的输出。如下例所示:

 1#!/bin/bash
 2
 3file="color"
 4for c in $(cat $file)
 5do
 6    echo "beautiful color:$c"
 7done
 8
 9# 结果
10[njust@njust tutorials]$ ./dana4.sh 
11beautiful color:Red
12beautiful color:Green
13beautiful color:Bule
14beautiful color:Pink

上述情况中每种颜色都在单独的一行,而不是通过空格分隔的。但是,这并没有解决数据中用空格的情况

1.5 更改字段分隔符

造成for命令中以空格为分隔符的原因是特殊的环境变量IFS(内部字段分隔符),IFS环境变量定义了bash shell用作字符分隔符的一些列字符。默认情况下,bash shell会将下面的字符当成字段分隔符:

a.空格

b.制表符

c.换行符

如果bash shell在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。这会导致在处理含有空格的数据时,显得十分麻烦。解决方法:可以在shell脚本中临时更改IFS环境变量的值来限制被bash shell当作字段分隔符的字符,如下例所示:
 1#!/bin/bash
 2
 3file="color"
 4
 5IFS=$'\n'   # 告诉bash shell在数据值中忽略空格和制表符,仅识别换行符!
 6
 7for c in $(cat $file)
 8do
 9    echo "beautiful color:$c"
10done
11
12# 结果
13[njust@njust tutorials]$ ./dana4.sh 
14beautiful color:Red
15beautiful color:Green
16beautiful color:Bule
17beautiful color:Pink
18beautiful color:Deep Purple

在处理代码量较大的脚本时,可能在一个地方需要修改IFS值。在脚本的其他地方继续沿用默认的IFS值。一个可行的方法是在改变IFS之前保存原来的IFS值,之后再恢复它,这样保证了在脚本的后续操作中继续使用的是默认的IFS值。

1IFS.OLD=$IFS
2
3IFS=$'\n'
4<在代码中使用新的IFS值>
5IFS=$IFS.OLD

IFS环境变量的其他一些绝妙用法,假如你要遍历一个文件中用冒号分隔的值(比如/etc/passwd文件),你需要做的就是将IFS设置为冒号IFS=:。

如果要指定多个IFS字符,只要将它们在赋值行串起来就行,如IFS=$'\n':;"。

1.6 用通配符读取目录

可以用for命令来自动遍历目录中的文件,进行此操作时,必须在文件名或路径名中使用通配符。它会强制shell使用文件扩展匹配。文件扩展匹配是生成匹配指定通配符的文件名或路径名的过程。如下例所示:

 1#!/bin/bash
 2
 3for file in /home/njust/tutorials/*
 4do
 5
 6    if [ -d "$file" ]  # 在linux中文件名或目录名中包含空格是合法的,要解决这个问题可以将$file变量用双引号圈起来。如果不这样的话遇到含空格的目录名或文件名时会报错!
 7    then
 8        echo "$file is a directory"
 9    elif [ -f "$file" ]
10    then
11        echo "$file is a file"
12    fi
13done
14
15# 结果
16[njust@njust tutorials]$ ./dana5.sh 
17/home/njust/tutorials/case.sh is a file
18/home/njust/tutorials/color is a file
19/home/njust/tutorials/curry.sh is a file
20/home/njust/tutorials/dana is a file
21/home/njust/tutorials/dana1.sh is a file
22/home/njust/tutorials/dana2.sh is a file
23/home/njust/tutorials/dana3.sh is a file
24/home/njust/tutorials/dana4.sh is a file
25/home/njust/tutorials/dana5.sh is a file
26/home/njust/tutorials/dana.sh is a file

也可以在for命令中列出多个目录通配符,将目录查找和列表合并进同一个for语句,如下例所示:

 1#!/bin/bash
 2
 3for file in /home/njust/.b* /home/njust/tutorials
 4do
 5
 6    if [ -d "$file" ]
 7    then
 8        echo "$file is a directory"
 9    elif [ -f "$file" ]
10    then
11        echo "$file is a file"
12    else
13        echo "$file doesn't exist"
14    fi
15done
16
17# 结果
18[njust@njust tutorials]$ ./dana6.sh 
19/home/njust/.bash_history is a file
20/home/njust/.bash_logout is a file
21/home/njust/.bash_profile is a file
22/home/njust/.bashrc is a file
23/home/njust/tutorials is a directory

2.C语言风格的for命令

bash shell也支持一种for循环,它看起来与C语言风格的for循环类似,但有一些细微的不同。bash中C语言风格的for循环基本格式如下
1for (( 变量初始化; 条件; 变量迭代变化 ))
注意:有些部分并没有遵循bash shell标准的for命令。如下所示:a.变量赋值可以有空格;b.条件中的变量不以美元符开头;c.迭代过程的算式未使用expr命令格式;下面是在bash shell程序中使用C语言风格的for命令:
 1#!/bin/bash
 2
 3for (( i = 1; i <= 10; i++ )) 
 4do 
 5    echo "The next number is $i"
 6done
 7
 8# 结果
 9[njust@njust tutorials]$ ./dana7.sh 
10The next number is 1
11The next number is 2
12The next number is 3
13The next number is 4
14The next number is 5
15The next number is 6
16The next number is 7
17The next number is 8
18The next number is 9
19The next number is 10
2.1 使用多个变量

C语言风格的for命令也允许为迭代使用多个变量。循环会单独处理每个变量,可以为每个变量定义不同的迭代过程,尽管可以使用多个变量,但只能在for循环中定义一种条件

 1#!/bin/bash
 2
 3# mutiple variables
 4
 5for (( a = 1, b = 10; a <= 10; a++, b-- ))
 6do 
 7    echo "$a - $b"
 8done
 9
10# 结果
11[njust@njust tutorials]$ ./dana8.sh 
121 - 10
132 - 9
143 - 8
154 - 7
165 - 6
176 - 5
187 - 4
198 - 3
209 - 2
2110 - 1

3.while命令

while命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的测试命令返回的是退出状态码0,它会在每次迭代的一开始测试test命令。在test命令返回非零退出状态码时,while命令会停止执行那组命令。
3.1 while的基本格式
1while test 命令
2do
3    命令
4done
while命令的关键点:所指定的test 命令的退出状态码必须随着循环中运行的命令而改变。如果退出状态码不发生改变,while循环就会一直不停地执行下去。最常见的test 命令的用法是用方括号检查循环命令中用到的shell变量的值,如下所示:
 1#!/bin/bash
 2
 3var1=10
 4while [ $var1 -gt 0 ]
 5do 
 6    echo $var1
 7    var1=$[ $var1 - 1 ]
 8done
 9
10# 结果
11[njust@njust tutorials]$ ./dana9.sh 
1210
139
148
157
166
175
184
193
202
211
3.2 使用多个测试命令

while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。如下例所示:

 1#!/bin/bash
 2
 3var=10
 4
 5while echo $var
 6    [ $var  -ge 0 ]
 7do
 8    echo "This is inside in the loop"
 9    var=$[ $var - 1 ]
10done
11
12# 结果
13[njust@njust tutorials]$ ./dana10.sh 
1410
15This is inside in the loop
169
17This is inside in the loop
188
19This is inside in the loop
207
21This is inside in the loop
226
23This is inside in the loop
245
25This is inside in the loop
264
27This is inside in the loop
283
29This is inside in the loop
302
31This is inside in the loop
321
33This is inside in the loop
340
35This is inside in the loop
36-1
在含有多个命令的while语句中,在每次迭代中所有测试命令都会被执行,包括测试命令失败的最后一次迭代。注意:指定多个测试命令时,每个测试命令都出现在单独的一行

4.until命令

until命令与while命令工作方式相反,until命令要求你指定一个通常返回非零状态码的测试命令。只有测试命令的退出状态码非零,bash shell才会执行循环中列出的命令。一旦测试命令返回退出状态码0,循环就结束了。until命令的基本格式如下所示:
1until test 命令
2do
3    命令
4done
可以在until命令语句中放入多个测试命令,只有最后一个命令的退出状态码决定了bash shell是否执行已定义的命令。如下例所示:
 1#!/bin/bash
 2
 3var=100
 4
 5until [ $var -eq 0 ]
 6do
 7    echo $var
 8    var=$[ $var - 25 ]
 9done
10
11# 结果
12[njust@njust tutorials]$ ./dana11.sh 
13100
1475
1550
1625
与while命令相似,until命令在使用多个测试命令时要注意。shell会执行指定的多个测试命令,只有在最后一个命令成立时才会停止。如下例所示:
 1#!/bin/bash
 2
 3var=100
 4
 5until echo $var
 6    [ $var -eq 0 ]
 7do
 8    echo "Inside the loop: $var"
 9    var=$[ $var - 25 ]
10done
11
12# 结果
13[njust@njust tutorials]$ ./dana12.sh 
14100
15Inside the loop: 100
1675
17Inside the loop: 75
1850
19Inside the loop: 50
2025
21Inside the loop: 25
220

5.嵌套循环

循环语句可以在循环内使用任意类型的命令,包括其他循环命令。这种循环称为嵌套循环。注意:使用嵌套循环时,在迭代中使用迭代,与命令运行的次数是乘积关系。如下例所示:

 1#!/bin/bash
 2
 3for (( a = 1; a <= 3; a++ ))
 4do
 5    echo "Starting loop $a:"
 6    for (( b = 1; b <= 3; b++ ))
 7    do
 8        echo "  Inside loop:$b"
 9    done
10done
11
12# 结果
13[njust@njust tutorials]$ ./dana13.sh 
14Starting loop 1:
15Inside loop:1
16Inside loop:2
17Inside loop:3
18Starting loop 2:
19Inside loop:1
20Inside loop:2
21Inside loop:3
22Starting loop 3:
23Inside loop:1
24Inside loop:2
25Inside loop:3
在混用循环命令时也一样,比如在while循环内部放置一个for循环。如下例所示:
 1#!/bin/bash
 2
 3var1=5
 4
 5while [ $var1 -ge 0 ]
 6do
 7    echo "Outer loop: $var1"
 8    for (( var2 = 1; $var2 < 3; var2++ ))
 9    do
10        var3=$[ $var1 * $var2 ]
11        echo "  Inner loop: $var1 * $var2 = $var3"
12    done
13    var1=$[ $var1 - 1 ]
14done
15
16# 结果
17[njust@njust tutorials]$ ./dana14.sh 
18Outer loop: 5
19Inner loop: 5 * 1 = 5
20Inner loop: 5 * 2 = 10
21Outer loop: 4
22Inner loop: 4 * 1 = 4
23Inner loop: 4 * 2 = 8
24Outer loop: 3
25Inner loop: 3 * 1 = 3
26Inner loop: 3 * 2 = 6
27Outer loop: 2
28Inner loop: 2 * 1 = 2
29Inner loop: 2 * 2 = 4
30Outer loop: 1
31Inner loop: 1 * 1 = 1
32Inner loop: 1 * 2 = 2
33Outer loop: 0
34Inner loop: 0 * 1 = 0
35Inner loop: 0 * 2 = 0

6.循环处理文件数据

通常必须遍历存储在文件中的数据,这要求结合已讲过的两种技术:

a.使用嵌套循环;

b.修改IFS环境变量;

通过修改IFS环境变量,就能强制for命令将文件中的每行都当成单独的一个条目来处理,即使数据中有空格也是如此。一旦从文件中提取出了单独的行,可能需要再次利用循环来提取行中的数据。典型的案例是处理/etc/passwd文件中的数据,逐行遍历/etc/passwd文件,并将IFS变量的值改成冒号,这样就能分割开每行中的各个数据段。如下例所示:
 1#!/bin/bash
 2
 3IFS.OLD=$IFS
 4
 5IFS=$'\n'
 6for var in $( cat /etc/passwd )
 7do
 8    echo "Values in $var -"
 9    IFS=:
10    for v in $var
11    do
12        echo "  $v"
13    done
14done
15
16# 结果
17[njust@njust tutorials]$ ./dana15.sh 
18Values in root:x:0:0:root:/root:/bin/bash -
19root
20x
210
220
23root
24/root
25/bin/bash

7.控制循环

7.1 break命令
break语句是退出循环的一个简单方法。可以使用break命令来退出任意类型的循环,包括while循环和until循环。跳出单个循环:在shell执行break命令时,它会尝试跳出当前正在执行的循环。
 1#!/bin/bash
 2
 3for var in 1 2 3 4 5 6 7 8 9 10
 4do
 5    if [ $var -eq 5 ]
 6    then
 7        break
 8    fi
 9    echo "Current number: $var"
10done
11echo "The for loop is completed"
12
13# 结果
14[njust@njust tutorials]$ ./dana16.sh 
15Current number: 1
16Current number: 2
17Current number: 3
18Current number: 4
19The for loop is completed
跳出单个循环的情景同样也适用于while和until循环,如下例所示:
 1#!/bin/bash
 2
 3var=1
 4
 5while [ $var -lt 10 ]
 6do
 7    if [ $var -eq 5 ]
 8    then
 9        break
10    fi
11    echo "Iteration: $var"
12    var=$[ $var + 1 ]
13done
14
15echo "The while loop is completed"
16
17# 结果
18[njust@njust tutorials]$ ./dana17.sh 
19Iteration: 1
20Iteration: 2
21Iteration: 3
22Iteration: 4
23The while loop is completed
跳出内部循环:在处理多个循环时,break命令会自动终止你所在的最内层的循环
 1#!/bin/bash
 2
 3for (( a =  1; a < 4; a++ ))
 4do
 5    echo "Outer loop:$a"
 6    for (( b = 1; b < 100; b++ ))
 7    do
 8        if [ $b -eq 5 ]
 9        then
10            break
11        fi
12        echo "  Inner loop:$b"
13    done
14done
15
16# 结果
17[njust@njust tutorials]$ ./dana18.sh 
18Outer loop:1
19    Inner loop:1
20    Inner loop:2
21    Inner loop:3
22    Inner loop:4
23Outer loop:2
24    Inner loop:1
25    Inner loop:2
26    Inner loop:3
27    Inner loop:4
28Outer loop:3
29    Inner loop:1
30    Inner loop:2
31    Inner loop:3
32    Inner loop:4
跳出外部循环:有时候你在内部循环,但需要停止外部循环。break命令接收单个命令行参数值:break n。n指定了要跳出的循环层级,默认情况下,n等于1,表示跳出的是当前的循环。如果n设置为2,break命令会停止下一级的外部循环。如下例所示:
 1#!/bin/bash
 2
 3for (( a = 1; a < 4; a++ ))
 4do
 5    echo "Outer loop:$a"
 6    for (( b = 1; b < 100; b++ ))
 7    do
 8        if [ $b -gt 4 ]
 9        then
10            break 2
11        fi
12        echo "  Inner loop:$b"
13    done
14done 
15
16# 结果
17[njust@njust tutorials]$ ./dana19.sh 
18Outer loop:1
19    Inner loop:1
20    Inner loop:2
21    Inner loop:3
22    Inner loop:4
7.2 continue命令
continue命令可以提前中止某次循环中的命令,但并不会完全终止整个循环。可以在循环内部设置shell不执行命令的条件。如下例所示:
 1#!/bin/bash
 2
 3for (( a = 1; a < 15; a++ ))
 4do
 5    if [ $a -gt 5 ] && [ $a -lt 10 ]
 6    then
 7        continue
 8    fi
 9    echo "Iteration number: $a"
10done
11
12# 结果
13Iteration number: 1
14Iteration number: 2
15Iteration number: 3
16Iteration number: 4
17Iteration number: 5
18Iteration number: 10
19Iteration number: 11
20Iteration number: 12
21Iteration number: 13
22Iteration number: 14
也可以在while或until循环中使用continue命令,但是特别小心。当shell执行continue命令时,它会跳过剩余的命令。如果你在其中某个条件里对测试条件变量进行增值,问题就会出现
 1#!/bin/bash
 2
 3var1=0
 4
 5while echo "while iteration: $var1"
 6      [ $var1 -lt 15 ]
 7do
 8    if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
 9    then
10        continue  # 当shell执行continue命令时,它跳过了while循环中余下的命令。
11    fi
12    echo "  Inside iteration number:$var1"
13    var1=$[ $var1 + 1 ]
14done
15
16# 结果
17[njust@njust tutorials]$ ./dana21.sh 
18while iteration: 0
19Inside iteration number:0
20while iteration: 1
21Inside iteration number:1
22while iteration: 2
23Inside iteration number:2
24while iteration: 3
25Inside iteration number:3
26while iteration: 4
27Inside iteration number:4
28while iteration: 5
29Inside iteration number:5
30while iteration: 6
31while iteration: 6
32while iteration: 6
33while iteration: 6
34while iteration: 6
35while iteration: 6
36while iteration: 6
37while iteration: 6
38while iteration: 6
39while iteration: 6
和break命令类似,continue命令也允许通过命令行参数指定要继续执行哪一级循环:continue n。其中n定义了要继续的循环层级,如下例所示:
 1#!/bin/bash
 2
 3for (( a = 1; a <= 5; a++ ))
 4do
 5    echo "Iteration: $a"
 6    for (( b = 1; b < 3; b++ ))
 7    do 
 8        if [ $a -gt 2 ] && [ $a -lt 4 ]
 9        then
10            continue 2 
11        fi
12        var=$[ $a * $b ]
13        echo "  The result of $a * $b is $var"
14    done
15done 
16
17# 结果
18[njust@njust tutorials]$ ./dana22.sh 
19Iteration: 1
20The result of 1 * 1 is 1
21The result of 1 * 2 is 2
22Iteration: 2
23The result of 2 * 1 is 2
24The result of 2 * 2 is 4
25Iteration: 3
26Iteration: 4
27The result of 4 * 1 is 4
28The result of 4 * 2 is 8
29Iteration: 5
30The result of 5 * 1 is 5
31The result of 5 * 2 is 10

8.处理循环的输出

在shell脚本中,可以对循环的输出使用管道或进行重定向。可以通过在done命令后添加一个处理命令来实现。如下例所示:

 1#!/bin/bash
 2
 3for file in /home/njust/*
 4do
 5    if [ -d "$file" ]
 6    then
 7        echo "$file is a directory"
 8    else
 9        echo "$file is a file"
10    fi
11done > output.txt
12
13# 结果
14[njust@njust tutorials]$ cat output.txt 
15/home/njust/sentinel is a file
16/home/njust/tutorials is a directory
17/home/njust/公共 is a directory
18/home/njust/模板 is a directory
19/home/njust/视频 is a directory
20/home/njust/图片 is a directory
21/home/njust/文档 is a directory
22/home/njust/下载 is a directory
23/home/njust/音乐 is a directory
24/home/njust/桌面 is a directory
上述方法同样适用于将循环的结果通过管道传给另一个命令,如下例所示:
 1#!/bin/bash
 2
 3for state in "North Dakota" Pink Blue Green Red
 4do
 5    echo "$state is the next place to go"
 6done | sort
 7
 8echo "This completes our travels"
 9
10# 结果
11[njust@njust tutorials]$ ./dana24.sh 
12Blue is the next place to go
13Green is the next place to go
14North Dakota is the next place to go
15Pink is the next place to go
16Red is the next place to go
17This completes our travels

9.循环在实际中的应用

查找可执行文件

 1#!/bin/bash
 2
 3IFS=:
 4
 5for folder in $PATH
 6
 7do
 8    echo "$folder"
 9    for file in $folder/*
10    do
11        if [ -x $file ]
12        then
13            echo "  $file"
14        fi
15    done
16done
17
18# 结果
19/sbin/vgextend
20/sbin/vgimport
21/sbin/vgimportclone
22/sbin/vgmerge
23/sbin/vgmknodes
24/sbin/vgreduce
25/sbin/vgremove
26/sbin/vgrename
27/sbin/vgs
28/sbin/vgscan
29/sbin/vgsplit
30/sbin/vigr
创建多个用户账户
 1#!/bin/bash
 2
 3input="users.txt"  # users.txt文件需提前创建,文件内容为:userid,username
 4
 5while IFS=',' read -r userid name
 6do
 7    echo "adding $userid"
 8    useradd -c "$name" -m $userid
 9
10done < "$input"
11
12# 结果,必须用root用户才能运行此脚本,因为useradd命令需要root权限!
13[root@njust tutorials]# ./dana26.sh
14adding C001
15adding D002
16adding H003
17adding J004
18
19# 验证
20[root@njust tutorials]# tail /etc/passwd
21postfix:x:89:89::/var/spool/postfix:/sbin/nologin
22ntp:x:38:38::/etc/ntp:/sbin/nologin
23tcpdump:x:72:72::/:/sbin/nologin
24njust:x:1000:1000:njust:/home/njust:/bin/bash
25shenchao:x:1001:1001::/home/shenchao:/bin/bash
26userid:x:1002:1003:name:/home/userid:/bin/bash
27C001:x:1003:1004:curry:/home/C001:/bin/bash
28D002:x:1004:1005:durant:/home/D002:/bin/bash
29H003:x:1005:1006:harden:/home/H003:/bin/bash
30J004:x:1006:1007:james:/home/J004:/bin/bash

10.资料下载

[1] https://github.com/cdlwhm1217096231/Linux/tree/master/Shell%E8%84%9A%E6%9C%AC%E7%BC%96%E7%A8%8B