Web Notes
2016.08.20
Using Liquid in Jekyll - Live with Demos
Liquid is a simple template language that Jekyll uses to process pages for your site. With Liquid you can output complex contents without additional plugins.
结构化循环命令在编程中很常见。通常需要重复一组命令直到触及某个特定条件。比如处理某个目录下的所有文件、系统上的所有用户或某个文本文件中的所有内容。bash shell 提供了三个常用的循环命令 for、while 和 until,就来好好研究研究吧。
for 命令允许你创建一个遍历一系列值的循环。每次迭代都使用其中一个值来执行已定义好的一组命令。
for var in list
do
commands
done
for 命令最基本的用法就是遍历值列表:
#!/bin/bash
for word in this is a just a sentence
do
echo the next word is $word
done
---
the next word is this
the next word is is
the next word is a
the next word is sentence
-
echo $word
---
sentence
每次 for 命令遍历值列表,它都会将列表中的下个值赋给 $word
变量。在最后一次迭代后,$word
变量的值会在 shell 脚本的剩余部分一直保持最后迭代的值。
但是,如果要遍历的值列表中含有特殊符号,例如单引号,shell 会把它当作一个单独的数据值,有两种方法可以解决:
另外一点,for 命令假定每个值都是用空格分隔的,如果值列表中含有词组这样的,也需要用双引号将其圈起来。
当然,多数时候我们会将这个值列表存储在某个变量中,然后需要遍历变量中具体成员。
for item in $list
任何能产生输出的命令,都可以使用 for 命令来遍历其输出:
#!/bin/bash
file="/path/to/file"
for item in $(cat $file)
do
echo $item
done
cat
的输出包含 file 的内容,for 命令会遍历 file 中的内容,for 命令怎样分隔内容,就要继续看下一节咯。
bash shell 中有一个特殊的环境变量,内部字段分隔符 IFS (internal field separator)。IFS 定义了 bash shell 用作字段分隔符的一系列字符。默认情况下,bash shell 会将以下字符当作字段分隔符:
每当 bash shell 看到了这些字符中的任意一个,它就会假定遇到了列表中的新数据字段的开始。在处理含有空格的数据(例如文件路径)时,就会遇到麻烦了。要解决这个问题,可以在 shell 脚本中临时更改 IFS 环境变量的值来限制 bash shell 的分隔。例如,使其只能识别换行符:
#!/bin/bash
IFSOLD=$IFS
IFS=$'\n'
file="/path/to/file"
for line in $(cat $file)
do
echo $line
done
IFS=$IFSOLD
如果要指定多个 IFS 字符,只要将它们在赋值行串起来就行:
IFS=$'\n':;"
#将换行符,冒号,分号和双引号设为IFS
遍历目录中的文件,也可以用 for 命令实现。
#!/bin/bash
for file in /home/ubuntu/workspace/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
---
/home/ubuntu/workspace/README.md is a file
/home/ubuntu/workspace/bash is a directory
/home/ubuntu/workspace/finance is a directory
/home/ubuntu/workspace/letsencrypt is a directory
在 test 语句中,我们将 $file
用双引号圈起来是为了适应含有空格的目录或文件名。
要遍历多个目录,可以将多个目录合并到同一个 for 语句中:
for file in /home/ubuntu/workspace/* /home/ubuntu/lib/*
即使文件或目录不存在,for 命令也会尝试处理列表中的内容,所以,在执行该命令之前测试一下文件或目录还是有必要的。
在 C 语言中,常用的 for 循环是这样的;
for (int i = 0; i <= 10; i++)
{
printf("The next number is %d\n", i);
}
bash shell 也支持这种风格的 for 循环,其基本格式为:
for (( varible assignment ; condition; iteration process ))
for (( a = 1; a <=10; a++ ))
在这种格式中,有些部分没有遵循 bash shell 标准的命令:
来举个🌰:
#!/bin/bash
for (( i = 1, j = 10; i <= 10; i++, j-- ))
do
echo "$i ~ $j"
done
---
1 ~ 10
2 ~ 9
3 ~ 8
4 ~ 7
5 ~ 6
6 ~ 5
7 ~ 4
8 ~ 3
9 ~ 2
10 ~ 1
while 命令允许定义一个要测试的命令,只要测试命令返回的退出状态码为 0
就循环执行一组命令。基本格式为:
while test command
do
other commands
done
while 命令的关键在于所指定的 test command 的退出状态码必须随着循环中运行的命令而改变,不然 while 命令将一直运行下去。
#!/bin/bash
var=10
while [ $var -gt 0 ]
do
echo $var
var=$[ $var-1 ]
done
while 命令允许你在 while 语句行定义多个测试命令,只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。例如:
#!/bin/bash
var=5
while echo $var
[ $var -ge 0 ]
do
echo "this is inside the loop"
var=$[ $var - 1 ]
done
---
5
this is inside the loop
4
this is inside the loop
3
this is inside the loop
2
this is inside the loop
1
this is inside the loop
0
this is inside the loop
-1
注意,把每个测试条件放在单独的一行上。这里如果是 while echo $var [$var -ge 0]
,会当作是一条 echo
语句执行,返回非 0
的退出状态码,循环将一直执行。
until 命令和 while 命令的工作方式完全相反。until 只有在测试命令返回非零的退出状态码时,循环中的命令才会执行。
#!/bin/bash
var=100
until [ $var -eq 0 ]
do
echo $var
var=$[ $var - 25 ]
done
---
100
75
50
25
在循环语句的循环体内可以使用任意类型的命令,当然也包括循环命令,这时候就是执行的一种嵌套循环(nested loop)。这种情况下,命令的运行次数是乘积关系,所以要十分注意多层嵌套以防消耗过多系统资源。
#!/bin/bash
var1=3
until [ $var1 -eq 0 ]
do
echo "outer loop: $var1"
var2=1
while [ $var2 -lt 5 ]
do
var3=$(echo "scale=2; $var1 / $var2" | bc)
echo " inner loop: $var1/$var2 = $var3"
var2=$[ $var2 + 1 ]
done
var1=$[ $var1 - 1 ]
done
---
outer loop: 3
inner loop: 3/1 = 3.00
inner loop: 3/2 = 1.50
inner loop: 3/3 = 1.00
inner loop: 3/4 = .75
outer loop: 2
inner loop: 2/1 = 2.00
inner loop: 2/2 = 1.00
inner loop: 2/3 = .66
inner loop: 2/4 = .50
outer loop: 1
inner loop: 1/1 = 1.00
inner loop: 1/2 = .50
inner loop: 1/3 = .33
inner loop: 1/4 = .25
bash shell 有两个命令可在循环中控制循环内部情况:
break
continue
使用这两个命令,就可以不用等到循环完成所有迭代,而是达到指定条件即可跳出循环。
1. 跳出单个循环
在执行到 break 命令时,它会尝试跳出当前的循环。
#!/bin/bash
for var in 1 2 3 4 5 6 7
do
if [ $var -eq 3 ]
then
break
fi
echo "iteration number: $var"
done
echo "the for loop finished"
---
iteration number: 1
iteration number: 2
the for loop finished
2. 跳出内部循环
在处理嵌套循环时,break 命令终止内层循环继续执行外层循环。
#!/bin/bash
for (( a = 1; a < 4; a++ ))
do
echo "outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -eq 5 ]
then
break
fi
echo " inner loop: $b"
done
done
---
outer loop: 1
inner loop: 1
inner loop: 2
inner loop: 3
inner loop: 4
outer loop: 2
inner loop: 1
inner loop: 2
inner loop: 3
inner loop: 4
outer loop: 3
inner loop: 1
inner loop: 2
inner loop: 3
inner loop: 4
3. 跳出外部循环
当处在内层循环而需要跳出外层循环时,可为 break 命令指定单个参数 break n
。默认情况下,n 为 1,表明跳出的是当前循环。如果 n 设为 2,break 命令会停止外一层的循环。
#!/bin/bash
for (( a = 1; a < 4; a++ ))
do
echo "outer loop: $a"
for (( b = 1; b < 100; b++ ))
do
if [ $b -gt 5 ]
then
break 2
fi
echo " inner loop: $b"
done
done
---
outer loop: 1
inner loop: 1
inner loop: 2
inner loop: 3
inner loop: 4
inner loop: 5
continue 会提前终止某次循环,但不会完全终止整个循环。
#!/bin/bash
for (( a = 1; a < 15; a++ ))
do
if [ $a -gt 5 ] && [ $a -lt 10 ]
then
continue
fi
echo "iteration number: $a"
done
---
iteration number: 1
iteration number: 2
iteration number: 3
iteration number: 4
iteration number: 5
iteration number: 10
iteration number: 11
iteration number: 12
iteration number: 13
iteration number: 14
在 while 或 until 命令中当然也可以使用 continue 命令。但是,如果在 continue 命令后改变测试条件变量的值就要非常小心了:
#!/bin/bash
var1=0
while echo "while iteration: $var1"
[ $var1 -lt 15 ]
do
if [ $var1 -gt 5 ] && [ $var1 -lt 10 ]
then
continue
fi
echo " inner iteration number: $var1"
var1=$[ $var1 + 1 ]
done
---
while iteration: 0
inner iteration number: 0
while iteration: 1
inner iteration number: 1
while iteration: 2
inner iteration number: 2
while iteration: 3
inner iteration number: 3
while iteration: 4
inner iteration number: 4
while iteration: 5
inner iteration number: 5
while iteration: 6
while iteration: 6
while iteration: 6
while iteration: 6
while iteration: 6
while iteration: 6
while iteration: 6
...
这个示例中,当执行到 continue 命令时,会跳过当次循环,也就跳过了 var1=$[ $var1 + 1 ]
这个重要的命令,使 while 命令一直执行下去。
在 shell 中,可以对循环的输出使用管道或进行重定向,在 done 命令后添加一个处理命令即可。
for file /home/frank/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
echo "$file is a file"
fi
done > output.txt
shell 会将 for 命令的输出重定向到 output.txt 文件中,而不显示在屏幕上。
好了,搞定 shell 中的循环会让我们在操作数据时事半功倍!多使用多练习就能体会到了。下次继续吧!
Frank Lin
Web Notes
2016.08.20
Liquid is a simple template language that Jekyll uses to process pages for your site. With Liquid you can output complex contents without additional plugins.
C Notes
2017.04.24
Understand the common sorting algorithms, just because recent take CS50 course.
JavaScript Notes
2018.12.17
JavaScript is a very function-oriented language. As we know, functions are first class objects and can be easily assigned to variables, passed as arguments, returned from another function invocation, or stored into data structures. A function can access variable outside of it. But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created? Also, what happens when a function invoked in another place - does it get access to the outer variables of the new place?