bash(中)

I/O

重定向操作符

重定向符 功能
`cmd1 cmd2` 管道;接受cmd1的标准输出,作为cmd2的标准输入
>file 将标准输出定向到file
<file file作为标准输入
>>file 将标准输出定向到file,将文件指针置于文件末尾
`> file` 强制重定向(忽略noclobber状态)
<>file 等效于<file >file
<<label 支持多行输入,直到输入为label为止
n>file 将文件描述符n定向到filen不写则默认为1
n<file 需要从文件描述符n中读数据时从file中读,n不写则默认为0
n>&m 将文件描述符n重定向到文件描述符m的文件中
n<&m m的输入作为n的输入
&>file 等价于>m 2>&1,即将标准输出和错误输出都重定向到file中
/dev/null 一个特殊的文件,只写不读,当需要丢弃输出时重定向到该文件

字符串IO

echo语句

选项开关

选项 功能
-e 打印转义字符的解释
-E (默认)不解释转义字符,直接打印
-n 末尾不打印换行符

read语句

  • 基本格式

    1
    read var1 var2 ...

    该语句从标准输入读取一行,以IFS为分隔符分别给var1 var2等赋值,如有多的字符串,则加在最后一个变量上。

  • 选项开关

选项 功能
-a 读取到数组中
-p str 输入前
-r 使用反斜线作为续行符(这将会忽略转义字符)
  • 很容易写出按行处理输入的脚本:

    1
    2
    3
    while read aLine; do
    # process the line
    done
  • read还很适合处理获取用户输入。根据惯例,提示应该被输出到标准错误。

语句块

1
2
3
4
5
{
while read aLine; do
# process the line
done
} < somefile

如上例,可以将一些代码置于{ }中,便组成了一个语句块,就像它们是一个命令一样。

上述代码实现了从文件读取内容,还有其它方案:

  • 包装成函数,调用函数时指明输入重定向

  • 包装成函数,在函数末尾指明输入重定向

    1
    2
    3
    4
    >   process_line() {
    > while ...
    > } < somefile
    >
  • while的末尾指明输入重定向

    1
    2
    3
    4
    >   while read aLine; do
    > ...
    > done < somefile
    >

这不仅对while...done适用,对任意流控制语句都适用,如if...fi while...done case...esac等等

刚刚发现{ }妙用的例子:假设a.out一运行就会报出segmentation fault的错误,如果使用

1
$ a.out &> b

会发现segmentation fault的报错仍然显示在屏幕上,没有被重定向至b文件。这是因为这行报错并不是a.out的标准输出或错误输出,而是由shell产生的(想一想signal机制)。要想丢弃这个错误,可以使用{ }

1
$ { a.out &> b; } &> /dev/null

命令行处理过程

基本步骤

  1. 分割成记号
  2. 检测每个命令的第一个记号的类型,查看是不是关键字
  3. 别名替换(只针对第一个关键字)
  4. 大括号扩展
  5. ~扩展(包括~+ ~user
  6. 参数扩展(解析$开头的变量)
  7. 命令替换($(string)
  8. 算数替换($((string))
  9. 单词分割(使用IFS作为分隔符)
  10. 通配符扩展
  11. 命令查询
  12. 执行

引用

  • 单引号直接绕过前十个步骤
  • 双引号绕过 1,2,3,4,9,10

命令查询次序

bash(上)中提到过,次序为函数、内置命令、脚本、可执行代码,有几个函数可以改变该次序:

command

删除函数查找(删除别名是其副作用)

builtin

只查找内置命令

enable

屏蔽一个内置命令(-n选项)

enable -n enable 还可以把自己干掉,哈哈哈。好在这时候还可以用builtin

再次执行:eval语句

eval后面可以跟多个参数,执行时会将它们拼接起来,按照上述12个步骤重新处理一遍

例一:间接执行

1
2
3
4
5
object=person
person=yfzm
eval obj1=\$$object
obj2=${!object}
# obj1: yfzm, obj2: yfzm

例二:自定义loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ cat myloop
loop() {
eval $@
if [ $cnt -gt 1 ]; then
cnt=$((cnt-1))
loop $@
fi
}

if [ -z $1 ]; then
echo "missing parameter"
exit 1
fi

if (($1<=0)); then
exit 0
fi

let cnt=$1
shift 1
loop $@

$ myloop 3 echo "hahaha"
hahaha
hahaha
hahaha