Appearance
呈现数据
理解输入输出
- 在显示器上输出
- 输出重定向到文件之中
都是对于全部的操作
标准文件描述
Linux把每一个对象当做文件处理, 包括输入输出进程, Linux使用文件描述符来表示每一个文件, 文件描述是一个非负数
可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符
bash shell保留了前三个文件描述符(0、1和2)
文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
STDIN
标准输入, 为键盘shell从STDIN得到的数据对应键盘的输入, 在使用重定向符号<的时候, Linux会用文件替换标准输入
没有指定输入的话, cat接收键盘上的输入
可以使用重定向把数据输入到任何接收标准输入的地方
STDOUT
标准输出, 在终端上就是显示器, 所有的输出都会定义为标准输出
通过输出重定向符号,通常会显示到显示器的所有输出会被shell重定向到指定的重定向文件。你也可以将数据追加到某个文件。这可以用>>符号来完成
输出的错误信息不会输入到文件中
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test1.sh > testout
./test1.sh: 行 2: asd:未找到命令
./test1.sh: 行 3: das:未找到命令
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout
STDERR
代表描述符的标准错误输出, 运行中的错误都会输出到这里, 通常情况下指向的是显示器, 不会随着重定向符号发生改变
重定向错误
在重定向的时候使用STDERR的描述符
只重定向错误
错误信号的文件描述符紧挨着重定向符号放置
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test1.sh 2> testout
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout
./test1.sh: 行 2: asd:未找到命令
./test1.sh: 行 3: das:未找到命令
可以用来区分正确的输出以及错误的输出
重定向错误和数据
如果想重定向错误和正常的信息, 要使用两个重定向符号
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls
test1.sh testout
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls -ah test1.sh badtest 2>test2 1>test3
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test2
ls: 无法访问 'badtest': 没有那个文件或目录
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test3
test1.sh
- 如果要输出到一个文件, 可以使用&>
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ ls -ah test1.sh badtest &>test2
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test2
ls: 无法访问 'badtest': 没有那个文件或目录
test1.sh
相较于标准输出, 这种方式会把错误信息的优先级设置为高于普通输出, 显示在最上边
在脚本中重定向输出
在脚本中可以使用STDOUT和STDIN文件描述在多个位置生成输出, 只要简单地重定向相应的文件就可以了
- 临时重定向
- 永久重定向脚本所有命令
临时重定向
有意在脚本中生成错误信息, 使用输出重定向符来将输出信息重定向到STDERR文件描述符, 必须在文件描述符数字之前加一个&
bash
echo"This is an error message" >&2
bash
1 #!/bin/bash
2
3 echo "This is an error" >&2
4 echo "This is an normal output"
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test2.sh
This is an error
This is an normal output
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test2.sh > test2
This is an error
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test2.sh 2> test2
This is an normal output
永久重定向
脚本中有大量的需要重定向的内容就会很麻烦, 但是你可以使用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符
bash
1 #!/bin/bash
2
3 exec 2>testerror
4
5 echo "This is the start of script"
6 echo "now redirecting all output to another location"
7
8 exec 1>testout
9
10 echo "This output should go to the testout file"
11 echo "but this should go to the testerror file" >&2
12
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test4.sh
This is the start of script
now redirecting all output to another location
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testerror
but this should go to the testerror file
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testout
This output should go to the testout file
一旦重定向了STDOUT或STDERR,就很难再将它们重定向回原来的位置。
在脚本中重定向输入
exec命令允许你将STDIN重定向到Linux系统上的文件中
bash
1 #!/bin/bash
2
3 exec 0< testfile
4 count=1
5 while read line
6 do
7 echo "Line #$count: $line"
8 count=$[ $count + 1 ]
9 done
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ test5.sh
Line #1: 11111
Line #2: 2222222
Line #3: 33333333333
Line #4: 44444444444444444
创建自己的重定向
可以吧文件描述符中的其他几个分配给其他的文件
创建输出文件描述符
bash
1 #!/bin/bash
2
3 exec 3>test6out
4 echo "This shoulf display on the monitor"
5 echo "stored in the file " >&3
6 echo "back"
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test6.sh
This shoulf display on the monitor
back
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test6out
stored in the file
也可以使用追加
exec 3>>testfile
, 在文件切换之后使用
重定向文件描述符
你可以分配另外一个文件描述符给标准文件描述符,反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符,然后再利用该文件描述符重定向回STDOUT。
bash
1 #!/bin/bash
2
3 exec 3>&1
4 exec 1>test7out
5 echo "This shoule store in the output file"
6 echo "along with this line"
7
8 exec 1>&3
9 echo "Now thing should back"
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test7.sh
Now thing should back
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat test7out
This shoule store in the output file
along with this line
创建文件输入描述符
bash
$ cat test15
#!/bin/bash
# redirecting input file descriptors
exec 6<&0
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$[ $count + 1 ]
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, this is the end.";;
esac
创建读写文件描述符
打开单个文件描述符来作为输入和输出
shell会维护一个内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。
bash
1 #!/bin/bash
2 exec 3<> testfile
3 read line <&3
4 echo "line: $line"
5 echo "This is a test line " >&3
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ myts.sh test8.sh
line: 11111
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile
11111
This is a test line
4444444444444444
关闭文件描述符
shell会在脚本退出时自动关闭它们。然而在有些情况下,你需要在脚本结束前手动关闭文件描述符
要关闭文件描述符,将它重定向到特殊符号&-。脚本中看起来如下:
bash
exec 3>&-
一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息
如果随后你在脚本中打开了同一个输出文件,shell会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件
列出打开的文件描述符
bash
lsof
许多Linux系统隐藏了该命令,这样用户就不会一不小心就发现了
有大量的命令行选项和参数可以用来帮助过滤lsof的输出。最常用的有-p和-d,前者允许指定进程ID(PID),后者允许指定要显示的文件描述符编号
要想知道进程的当前PID,可以用特殊环境变量$$(shell会将它设为当前PID)。-a选项用来对其他两个选项的结果执行布尔AND运算,这会产生如下输出
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 5039 jiao 0u CHR 136,0 0t0 3 /dev/pts/0
bash 5039 jiao 1u CHR 136,0 0t0 3 /dev/pts/0
bash 5039 jiao 2u CHR 136,0 0t0 3 /dev/pts/0
因为STDIN、STDOUT和STDERR文件描述符都指向终端,所以输出文件的名称就是终端的设备名
- COMMAND 正在运行的命令名的前9个字符
- PID 进程的PID
- USER 进程属主的登录名
- FD 文件描述符号以及访问类型(r代表读,w代表写,u代表读写)
- TYPE 文件的类型(CHR代表字符型,BLK代表块型,DIR代表目录,REG代表常规文件)
- DEVICE 设备的设备号(主设备号和从设备号)
- SIZE/OFF 如果有的话,表示文件的大小
- NODE 本地文件的节点号
- NAME 文件名
阻止命令输出
如果在运行在后台的脚本出现错误消息,shell会通过电子邮件将它们发给进程的属主
可以将STDERR重定向到一个叫作null文件的特殊文件
在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示
也可以在输入重定向中将/dev/null作为输入文件。由于/dev/null文件不含有任何内容,程序员通常用它来快速清除现有文件中的数据,而不用先删除文件再重新创建
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile
11111
This is a test line
4444444444444444
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat /dev/null > testfile
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile
创建临时文件
Linux使用/tmp文件夹存放不需要永久保存的文件, 大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件
任何用户账户都有权限在读写/tmp目录中的文件。这个特性为你提供了一种创建临时文件的简单方法,而且还不用操心清理工作
mktemp命令可以在/tmp目录中创建一个唯一的临时文件。shell会创建这个文件,但不用默认的umask值(参见第7章)。它会将文件的读和写权限分配给文件的属主,并将你设成文件的属主。一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然,root用户除外)
创建本地临时文件
mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ mktemp testing.XXXXXX
testing.FaEYfs
输出的就是创建的文件名, 可以保存在变量之中在后面使用
在/tmp目录创建临时文件
-t选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时,mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名
bash
1 #!/bin/bash
2
3 tempfile=$(mktemp -t tmp.XXXXXX)
4 echo "This is a test file." > $tempfile
5 echo "This is the second line." >> $tempfile
6
7 echo "The temp file is located at: $tempfile"
8 cat $tempfile
9 rm -f $tempfile
创建临时目录
-d选项告诉mktemp命令来创建一个临时目录而不是临时文件。这样你就能用该目录进行任何需要的操作了,比如创建其他的临时文件
bash
1 #!/bin/bash
2
3 tempdir=$(mktemp -d dir.XXXXXX) # 在本地创建一个文件夹
4 cd $tempdir
5 tempfile1=$(mktemp temp.XXXXXX)
6 tempfile2=$(mktemp temp.XXXXXX)
7 exec 7> $tempfile1
8 exec 8> $tempfile2
9
10 echo "Sending data to dictory $tempdir"
11 echo "This is a test line of data for $tempfile1" >&7
12 echo "This is a test line of data for $tempfile2" >&8
记录消息
把输出的信息同时发送到显示器以及日志文件
bash
tee filename
它将从STDIN过来的数据同时发往两处。一处是STDOUT,另一处是tee命令行所指定的文件名
写入的时候会覆盖原来的内容, 要是追加的话用-a
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ who | tee testfile
jiao tty2 2022-08-07 21:10 (tty2)
jiao@jiao-virtual-machine:~/桌面/linux-shell/15$ cat testfile
jiao tty2 2022-08-07 21:10 (tty2)
bash
1 #!/bin/bash
2
3 tempfile=test12out
4
5 echo $(date)| tee $tempfile
6 echo $(ls -alh)| tee -a $tempfile
7 echo $(who)| tee -a $tempfile
示例
bash
$cat test23
#!/bin/bash
# read file and create INSERT statements for MySQL
outfile='members.sql'
IFS=','
while read lname fname address city state zip
do
cat >> $outfile << EOF
INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('$lname', '$fname', '$address', '$city', '$state', '$zip');
EOF
done < ${1} # 读取文件
cat >> $outfile << EOF 先把输入的内容使用cat打印, 然后输入到文件中