首页 > 代码库 > 《学习bash》笔记--输入输出

《学习bash》笔记--输入输出

1.I/O重定向符

I/O重定向符如下:

  • cmd1 | cmd2:管道,接收cmd1的标准输出作为cmd2的标准输入。
  • >file:将标准输出定向到file
  • <file:从file接收标准输入
  • >>file:将标准输出定向到file,如果file存在则附加在后面
  • >|file:即使设置了noclobber仍然强制标准输出到file。

shell提供了一种称为noclobber的特性,该特性可防止重定向时不经意地重写了已存在的文件。通过设置变量noclobber可以

将此特 性打开。打开后若将输出重定向到某个已存在文件,则shell将报告错误消息,并且不执行重定向命令。

例如:

root@-virtual-machine:~# cat a.txt
123
root@virtual-machine:~# set -o | grep "noclobber"
noclobber       off
root@virtual-machine:~# echo "test1" > a.txt
root@virtual-machine:~# cat a.txt
test1
root@virtual-machine:~# set -o noclobber
root@virtual-machine:~# set -o | grep "noclobber"
noclobber       on
root@gmdz-virtual-machine:~# echo "hello" > a.txt
-bash: a.txt: 无法覆盖已存在的文件

root@virtual-machine:~# echo "hello" >| a.txt
root@virtual-machine:~# cat a.txt
hello

  • n>|file:即使设置了noclobber仍强制从文件描述符n中输出到file。

例如:

root@-virtual-machine:~# cat a.txt
123
root@virtual-machine:~# set -o | grep "noclobber"
noclobber       off
root@virtual-machine:~# ls nosuchfile 2> a.txt
root@virtual-machine:~# cat a.txt
ls: 无法访问nosuchfile: 没有那个文件或目录
root@virtual-machine:~# echo "123" > a.txt
root@virtual-machine:~# cat a.txt
123
root@virtual-machine:~# set -o noclobber
root@virtual-machine:~# set -o | grep "noclobber"
noclobber       on
root@virtual-machine:~# ls nosuchfile 2> a.txt
-bash: a.txt: 无法覆盖已存在的文件
root@virtual-machine:~# cat a.txt
123
root@virtual-machine:~# ls nosuchfile 2>| a.txt
root@virtual-machine:~# cat a.txt
ls: 无法访问nosuchfile: 没有那个文件或目录

  • <>file:使用file同时作为输入和输出。
主要用于设备文件,也就是对应诸如终端和通信线路的硬件设备,底层系统程序员可以使用它测试设备驱动器,否则,它不是很有用。
  • n<>file,以读写方式打开file,并将n重定向到该文件。

要说明这个重定向符之前,先要说明exec命令:

shell的内建命令exec将并不启动新的shell,而是用要被执行命令替换当前的shell进程,并且将老进程的环境清理掉,而且exec命令

后的其它命令将不再执行。因此,如果你在一个shell里面,执行exec ls那么,当列出了当前目录后,这个shell就自己退出了,因为这个

shell进程已被替换为仅仅执行ls命令的一个进程,执行结束自然也就退出了。不过,要注意一个例外,当exec命令来对文件描述符操

作的时候,就不会替换shell,而且操作完成后,还会继续执行接下来的命令。

  1. $ exec 3<>file  
  2. $ ls >&3  
  3. $ cat file  
  4. file  
  5. $ cat <&3  
  6. $ exec 3<>file  
  7. $ cat <&3  
  8. file
将文件描述符3定向到file,然后把标准输出定向到3以后,标准输出的内容就写入到file中。同样从标准输入读的时候,将标准输入定向

到3,会打印出file的内容。由上面的结果还能看到exec 3<> file只针对一条指令有效。

  • n>file,以写式打开file,并将n重定向到该文件。

例如:

root@gmdz-virtual-machine:~/test# exec 3> file
root@gmdz-virtual-machine:~/test# ls >& 3
root@gmdz-virtual-machine:~/test# cat file
a
b
root@gmdz-virtual-machine:~/test# cat <& 3
cat: -: 错误的文件描述符

  • n<file,以读式打开file,并将n重定向到该文件。

例如:

root@virtual-machine:~/test# cat file
test
root@virtual-machine:~/test# exec 3< file
root@virtual-machine:~/test# ls >& 3
ls: 写入错误: 错误的文件描述符
root@virtual-machine:~/test# cat <& 3
test
root@virtual-machine:~/test# cat <& 3

root@virtual-machine:~/test#

同样,exec 3< file只针对一条指令有效。

  • <<label:here-document

<<label重定向符强制一个命令的输入使用shell的标准输入,并一直读到仅含label的行。期间的输入称为here-document

例如:

# cat > msgfile << EOF
> 1
> 2
> 3
> EOF
root@gmdz-virtual-machine:~/test# cat msgfile
1
2
3

重定向<<有两个变体,首先,你可以通过把label括在单引号或双引号内防止shell进行参数和命令替换,例如:

root@virtual-machine:~/test# cat << EOF
> $PWD
> EOF
/root/test
root@virtual-machine:~/test# cat << "EOF"
> $PWD
> EOF
$PWD

第二个变体是<<-,它从here-document和标签行中删除前导TAB(但不删除空格)。

例如脚本内容如下:
#!/bin/bash
cat <<- EOF
      123
      456
EOF

执行后,输出为:

123
456
  • n>> file:将文件描述符n定向到file,如果file存在则附加到后面,例如:command 2>> file
  • n>& :将标准输出复制到文件描述符n
  • n<& :从文件描述符n复制标准输入
  • n>&m:使文件描述符n成为输出文件描述符m的副本

例如:

root@virtual-machine:~/test# cat file
test
root@virtual-machine:~/test# ls nosuchfile
ls: 无法访问nosuchfile: 没有那个文件或目录
root@virtual-machine:~/test# ls nosuchfile > file 2>&1
root@virtual-machine:~/test# cat file
ls: 无法访问nosuchfile: 没有那个文件或目录

  • n<&m:使文件描述符n成为输入文件描述符m的副本
  • &>file:定向标准输出和错误输出到file。

例如:

root@virtual-machine:~/test# ll a nosuchfile &> file
root@virtual-machine:~/test# cat file
ls: 无法访问nosuchfile: 没有那个文件或目录
-rw-r--r-- 1 root root 0 2014-08-01 09:09 a

  • <&-:关闭标准输入

例如有如下脚本:

exec <&-
cat

执行结果:

# ./a.sh
cat: -: 错误的文件描述符
cat: 关闭标准输入: 错误的文件描述符

  • >&-:关闭标准输出

例如有如下脚本:

exec >&-
echo "test"

执行结果:

# ./a.sh
./a.sh: 第 2 行: echo: 写错误: 错误的文件描述符

  • n>&-:关闭从文件描述符n的输出

例如:

root@gmdz-virtual-machine:~# ls
a.sh  file  test

root@virtual-machine:~# exec 3<> file
root@virtual-machine:~# ls >&3

root@virtual-machine:~# exec 3<> file
root@virtual-machine:~# exec 3>&-
root@virtual-machine:~# ls >&3
-bash: 3: 错误的文件描述符

  • n<&-:关闭从文件描述符n的输入

例如:

root@gmdz-virtual-machine:~# exec 3<> file
root@gmdz-virtual-machine:~# cat <&3
a.sh
file
test
root@virtual-machine:~# exec 3<> file
root@virtual-machine:~# exec 3<&-
root@virtual-machine:~# cat <&3
-bash: 3: 错误的文件描述符


2.字符串I/O

2.1.echo

echo将其参数简单地打印到标准输出上。echo接收短划线选项,如下表。

-e:打开斜线转义字符的解释

-E:关闭子系统上反斜线转义字符的解释,此为默认模式。

-n:省略最后的换行

例如:

root@virtual-machine:~# echo -e "a\ta"
a       a
root@virtual-machine:~# echo -E "a\ta"
a\ta
root@virtual-machine:~# echo -n "a\ta"
a\taroot@virtual-machine:~#


2.2.read

read允许你将值读到shell变量中,基本语法是:

read var1 var2 ...

该语句从标准输入中接收一行,将其分开形成单词,单词间用环境变量IFS的值中的任意字符分隔。单词然后被赋值到变量

var1,var2...

例如,a.sh的内容为:

read v1 v2
echo $v1
echo $v2

执行结果:

# ./a.sh
123 456
123
456

如果单词比变量多,则额外的单词被赋值到最后的变量中,如果你省略了变量,则整个输入被赋值到变量REPLY中。

例如,再次执行上面的脚本:

# ./a.sh
1 2 3 4
1
2 3 4

修改a.sh的内容为:

read
echo $REPLY

执行结果为:

# ./a.sh
1 2 3 4
1 2 3 4


下面介绍如下从文件中读取变量。

例如,file文件内容如下:

arg1-1 arg1-2
arg2-1 arg2-2
arg3-1 arg3-2

方法一:

使用如下a.sh脚本可以进行变量的读取:

while read v1 v2;do
echo $v1 $v2
done

执行结果:

# ./a.sh < file
arg1-1 arg1-2
arg2-1 arg2-2
arg3-1 arg3-2


方法二:

修改a.sh:

while read v1 v2;do
echo $v1 $v2
done < file

执行结果:

# ./a.sh
arg1-1 arg1-2
arg2-1 arg2-2
arg3-1 arg3-2


方法三:

修改a.sh:

func(){
while read v1 v2;do
echo $v1 $v2
done
}
func < file

执行结果:

# ./a.sh
arg1-1 arg1-2
arg2-1 arg2-2
arg3-1 arg3-2


方法四:

修改a.sh:

{
while read v1 v2;do
echo $v1 $v2
done
} < file

执行结果:

# ./a.sh
arg1-1 arg1-2
arg2-1 arg2-2
arg3-1 arg3-2


read有3个常用选项:-a,-p和-r。

-a允许你将值读到一个数组中,每个连续的条目读取都被赋值到以下标0开始的给定数组中,例如a.sh内容如下:
read -a people
echo ${people[0]} ${people[1]} ${people[2]}

执行结果:

# ./a.sh
1 2 3
1 2 3

-p选项在读取输入前打印该字符串,例如:

[root@yanPC ~]# read -p "dir?" dirname
dir?/home
[root@yanPC ~]# echo $dirname
/home

-r选项在读取输入时不忽略“\”字符。

如果"\"在输入字符串的内部:

[root@yanPC ~]# read dirname
123\456
[root@yanPC ~]# echo $dirname
123456
[root@yanPC ~]# read -r dirname
123\456
[root@yanPC ~]# echo $dirname
123\456

如果"\"在输入字符串的最后:

[root@yanPC ~]# read dirname
123\
> 456
[root@yanPC ~]# echo $dirname
123456
[root@yanPC ~]# read -r dirname
123\
[root@yanPC ~]# echo $dirname
123\