《Shell十三问》笔记(下)

继续开始shell十三问中11-13问和后续补充的笔记,加油!

(14)输入重定向与输出重定向

  • “>”是标准输出重定向,可以把输出结果送入文件
  • “<”是标准输入重定向,可以重新指定文件的内容作为输入
  • “2>”是标准错误重定向,可以把命名执行出现的错误提示送入文件

如果你想把标准输出和标准错误输出到一个文件中,可以使用如下的方法

  • $ ls my.file no.such.file 1>file.both 2>&1 (错误到输出)
  • $ ls my.file no.such.file 2>file.both >&2(输出到错误)

使用“<<”追加写入,“<”为最后一次写入的内容

(15)锁定文件避免覆盖写入

$ set -o noclobber
$ echo "4" > file.out
-bash: file: cannot overwrite existing file

如上所示即可避免覆盖写入,当想再次使用后

$ set +o noclobber
$ echo "5" > file.out
$ cat file.out
5

如果想临时的覆盖文档内容,而不取消限制,那么可以使用”>|”

$ set -o noclobber
$ echo "6" >| file.out
$ cat file.out
6

来看一道题,为什么file文件为空

$ echo "some text here" > file
$ cat < file
some text here
$ cat < file > file.bak
$ cat < file.bak
some text here
$ cat < file > file
$ cat < file

前面提到:$ cat < file > file 之后原本有内容的档案结果却被洗掉了﹗ 要理解这一现像其实不难,这只是 priority 的问题而已: * 在 IO Redirection 中,stdout 与 stderr 的管道会先准备好,才会从 stdin 读进资料。 也就是说,在上例中,> file 会先将 file 清空,然后才读进 < file , 但这时候档案已经被清空了,因此就变成读不进任何资料了…

2个问题

$ cat <> file (如果说前面的用优先级解释,但是这个不理解,暂时放一放吧)
$ cat < file >> file

(16)管道内保存数据

在 cm1 | cm2 | cm3 … 这段 pipe line 中,若要将 cm2 的结果存到某一档案呢? 若你写成 cm1 | cm2 > file | cm3 的话, 那你肯定会发现 cm3 的 stdin 是空的﹗(当然啦,你都将水管接到别的水池了﹗) 聪明的你或许会如此解决:

cm1 | cm2 > file ; cm3 < file

虽然这样可行,但是文件I/O会加倍,严重影响效率,所以解决方法是使用tee命令,如下:

cm1 | cm2 | tee file | cm3

(17)几个条件判断

&&与||实现

comd1 && {
 comd2
 comd3
 :
} || {
 comd4
 comd5
}

if...than...else句式

if comd1
then
    comd2
    comd3
else
    comd4
    comd5
fi


if comd1; then
    comd2
elif comd3; then
    comd4
else
    comd5
fi

这个的意思是:意思是说: 若 comd1 为 true ,然则执行 comd2 ﹔否则再测试 comd3 ,然则执行 comd4 ﹔倘若 comd1 与 comd3 均不成立,那就执行 comd5 。

提别一提的是,当than后面不需要接命令的话,可以使用“:”符号占位,这里就不多记录了,和C语言一样,多多的书写实践还能写出得心应手的脚本。

(18)循环

原文写的很简单易懂,我就直接摘过来了~

最后要介绍的是 shell script 设计中常见的”循环”(loop)。 所谓的 loop 就是 script 中的一段在一定条件下反覆执行的代码。 bash shell 中常用的 loop 有如下三种: for while * until

for loop 是从一个清单列表中读进变量值,并”依次”的循环执行 do 到 done 之间的命令行。 例:

in句式

for var in one two three four five
do
    echo -----------
    echo '$var is '$var
    echo
done

上例的执行结果将会是: 1) for 会定义一个叫 var 的变量,其值依次是 one two three four five 。 2) 因为有 5 个变量值,因此 do 与 done 之间的命令行会被循环执行 5 次。 3) 每次循环均用 echo 产生三行句子。 而第二行中不在 hard quote 之内的 $var 会依次被替换为 one two three four five 。 4) 当最后一个变量值处理完毕,循环结束。

我们不难看出,在 for loop 中,变量值的多寡,决定循环的次数。 然而,变量在循环中是否使用则不一定,得视设计需求而定。 倘若 for loop 没有使用 in 这个 keyword 来指定变量值清单的话,其值将从 $@ (或 $* )中继承:

for var; do
....
done

for loop 用于处理”清单”(list)项目非常方便, 其清单除了可明确指定或从 positional parameter 取得之外, 也可从变量替换或命令替换取得… (再一次提醒:别忘了命令行的”重组”特性﹗) 然而,对于一些”累计变化”的项目(如整数加减),for 亦能处理:

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

除了 for loop ,上面的例子我們也可改用 while loop 來做到:

num=1
while [ "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done

while loop 的原理与 for loop 稍有不同: 它不是逐次处理清单中的变量值,而是取决于 while 后面的命令行之 return value : 若为 ture ,则执行 do 与 done 之间的命令,然后重新判断 while 后的 return value 。 若为 false ,则不再执行 do 与 done 之间的命令而结束循环。

分析上例: 1) 在 while 之前,定义变量 num=1 。 2) 然后测试(test) $num 是否小于或等于 10 。 3) 结果为 true ,于是执行 echo 并将 num 的值加一。 4) 再作第二轮测试,此时 num 的值为 1+1=2 ,依然小于或等于 10,因此为 true ,继续循环。 5) 直到 num 为 10+1=11 时,测试才会失败… 于是结束循环。

我们不难发现: 若 while 的测试结果永远为 true 的话,那循环将一直永久执行下去:

while :; do
    echo looping...
done

一旦你能够理解 while loop 的话,那,就能理解 until loop : 与 while 相反,until 是在 return value 为 false 时进入循环,否则结束。 因此,前面的例子我们也可以轻鬆的用 until 来写:

num=1
until [ ! "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done

或是

num=1
until [ "$num" -gt 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done

(19)break与continue(与C相同)

这两个命令常用在複合式循环裡,也就是在 do … done 之间又有更进一层的 loop , 当然,用在单一循环中也未尝不可啦… ^_^

break 是用来打断循环,也就是”强迫结束” 循环。 若 break 后面指定一个数值 n 的话,则”从裡向外”打断第 n 个循环, 预设值为 break 1 ,也就是打断当前的循环。 在使用 break 时需要注意的是, 它与 return 及 exit 是不同的: break 是结束 loop return 是结束 function * exit 是结束 script/shell

而 continue 则与 break 相反:强迫进入下一次循环动作。 若你理解不来的话,那你可简单的看成:在 continue 到 done 之间的句子略过而返回循环顶端… 与 break 相同的是:continue 后面也可指定一个数值 n ,以决定继续哪一层(从裡向外计算)的循环, 预设值为 continue 1 ,也就是继续当前的循环。

在 shell script 设计中,若能善用 loop ,将能大幅度提高 script 在複杂条件下的处理能力。 请多加练习吧….

(20)[^ ] 跟 [! ]

通配符(wildcard)知识,暂不记录 “^”是正则表达式中符号,”!”是通配符中的符号,都表示”非”

(21)eval

eval是完成命令重组后再一次重组

a=1
A1=abc
eval echo \$A$a

(22)正则表达式 正则表达式(Regular Expression)知识,暂不记录 (23)grep与egrep与fgrep区别 * grep: 传统的 grep 程式, 在没有参数的情况下, 只输出符合 RE 字串之句子. 常见参数如下: -v: 逆反模示, 只输出”不含” RE 字串之句子. -r: 递迴模式, 可同时处理所有层级子目录裡的文件. -q: 静默模式, 不输出任何结果(stderr 除外. 常用以获取 return value, 符合为 true, 否则为 false .) -i: 忽略大小写. -w: 整词比对, 类似 \<word> . -n: 同时输出行号. -c: 只输出符合比对的行数. -l: 只输出符合比对的文件名称. -o: 只输出符合 RE 的字串. (gnu 新版独有, 不见得所有版本都支持.) -E: 切换为 egrep .

  • egrep: 为 grep 的扩充版本, 改良了许多传统 grep 不能或不便的操作. 比方说:
  • grep 之下不支持 ? 与 + 这两种 modifier, 但 egrep 则可.
  • grep 不支持 a|b 或 (abc|xyz) 这类”或一”比对, 但 egrep 则可.
  • grep 在处理 {n,m} 时, 需用 { 与 } 处理, 但 egrep 则不需. 诸如此类的… 我个人会建议能用 egrep 就不用 grep 啦… ^_^

  • fgrep: 不作 RE 处理, 表达式仅作一般字串处理, 所有 meta 均失去功能.