六、数据呈现

目录

6.1 理解输入输出

6.1.1 标准文件描述符

1 STDIN (0)

2 STDOUT (1)

3 STDERR(2)

6.1.2 重定向错误

1 只重定向错误

2 重定向错误和数据

6.2 在脚本中重定向输出

6.2.1 临时重定向

6.2.2 永久重定向

6.3 在脚本中重定向输入

6.4 创建自己的重定向

6.4.1 创建输出文件描述符

6.4.2 重定向文件描述符

6.4.3 创建输入文件描述符

6.4.4 创建读写文件描述符

6.4.5 关闭文件描述符

6.5 列出打开的文件描述符

6.6 阻止命令输出

6.7 创建临时文件

6.7.1 创建本地临时文件

6.7.2 在/tmp创建临时文件

6.7.3 创建临时目录

6.8 记录消息

6.9 实例


6.1 理解输入输出

目前为止,你已经知道了两种脚本输出的方法:

在显示器屏幕上显示输出

将输出重定向到文件中

6.1.1 标准文件描述符

Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符(filedescriptor)来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开的文件。每个进程一次最多可以有9个文件描述符。处于特殊目的,bash shell 保留了前三个文件描述符(0,1和2)。

1 STDIN (0)

STDIN 文件描述符代表shell的标准输入,在终端界面来说,标准输入是键盘。在使用 < 符号时,Linux会用重定向指定的文件来替换标准输入文件描述符,它会读取文件并提取数据,就像使用键盘输入的一样。

[root@kittod ~]# cat
test
test
haha 
haha

你可以通过STDIN重定向符号强制cat命令接受来自另一个非标准输入文件的输入

[root@kittod ~]# cat < ssh_port.sh
#!/bin/bash
while read line
do
  IP=$(echo $line |awk '{print $1}')
  PORT=$(echo $line | awk '{print $2}')
  echo "IP: $IP, PORT: $PORT"
  ssh root@$IP
done < iplist

现在cat命令会用ssh_port.sh文件中的行作为输入。

2 STDOUT (1)

STDOUT 文件描述符代表shell的标准输出。在终端界面上,标准输出也就时终端显示器。

shell的所有输出会被定向到标准输出中,也就是显示器。

默认情况下,大部分bash命令会将输出导向STDOUT文件描述符。你也可以通过输出重定向将命令结果导出到指定文件。

[root@kittod ~]# ls
3.sh 88.sh del_user2.sh file02.sh for_continue.sh 
list3.sh log.txt select.sh ssh_port.sh
4.sh 99.sh del_user.sh file.sh iplist 
list4.sh online_host.sh sjx2.sh yanghui.sh
5.sh add_user.sh file for_break.sh list2.sh list.sh
 price.sh sjx.sh
[root@kittod ~]# ls > hehe.test

>会覆盖,>>是在下面添加

你也可以使用 >> 来表示追加重定向,将命令的输出结果追加到指定文件的末尾。

[root@kittod ~]# ls >> hehe.test

如果你对脚本使用了标准输出重定向,你会遇到一个问题,如下面的案例描述的情况:

[root@kittod ~]# ls hehe > file
ls: cannot access 'hehe': No such file or directory
[root@kittod ~]# cat file

当命令生成错误消息时,shell并未将错误消息通过重定向输出到重定向指定的文件。shell创建了输出重定向文件,但错误消息却显示在了屏幕上。注意,在显示file文件的时候并没有任何错误,因为file文件已经创建成功,只是文件内容是空的。shell对于错误消息的处理是跟普通输出分开的。如果你创建了在后台模式下运行的shell脚本,通常你必须依赖发送到日志文件的输出消息。用这种方法的话,如果出现了错误信息,这些信息是不会出现在日志文件中。你需要换种方法来处理。

3 STDERR(2)

shell通过特殊的 STDERR 文件描述符来处理错误消息。STDERR 文件描述符代表shell的标准错误输出。shell或者shell中运行的程序和脚本出错时生成的错误消息都会发送到这个位置。

默认情况下,STDERR文件描述符会和STDOUT文件描述符指向同样的地方,尽管分配的描述符不一样,也就是说,默认情况,错误消息也会显示到显示器输出中。

但从例子也可以看出,STDERR并不会随着STDOUT的重定向而发生改变,使用脚本时,你可能需要改变这种行为,尤其是当你希望将错误消息保存到日志文件中的时候。

6.1.2 重定向错误

重定向标准错误输出的方法和STDOUT的方法没太大差别,只需要指定STDERR描述符即可,

有以下几种方法:

1 只重定向错误

[root@kittod ~]# ls hehe 2> file
[root@kittod ~]# cat file
ls: cannot access 'hehe': No such file or directory
混合显示
[root@kittod ~]# ls hehe iplist 2> log.txt
iplist
[root@kittod ~]# cat log.txt
ls: cannot access 'hehe': No such file or directory

2 重定向错误和数据

[root@kittod ~]# ls hehe iplist 2> log1.txt 1> log2.txt  #错误给log1.提信徒,正确的给log2.txt
[root@kittod ~]# cat log1.txt
ls: cannot access 'hehe': No such file or directory
[root@kittod ~]# cat log2.txt
iplist
你可以使用提供的特殊符号&>
[root@kittod ~]# ls hehe iplist &> log3.txt
[root@kittod ~]# cat log3.txt
ls: cannot access 'hehe': No such file or directory
iplist

6.2 在脚本中重定向输出

可以在脚本中用STDOUT和STDERR文件描述符以在多个位置生成输出,只要简单地重定向相应的文件描述符就可以,有两种方法在脚本中重定向输出:

临时重定向行输出

永久重定向脚本中的所有命令

6.2.1 临时重定向

如果有意在脚本中生成错误消息,可以将单独的一行输出重定向到STDERR。你所需要坐的是使用输出重定向符来将输出信息重定向到STDERR文件描述符。在重定向到文件描述符时,你必须在文件描述符数字之前加一个&。

[root@kittod ~]# cat error01.sh
#!/bin/bash

echo "This is an error message" >&2

这样就会在脚本的STDERR文件描述符所指向的位置显示文本,而不是通常的STDOUT,下面的例子就利用了该功能

[root@kittod ~]# cat error02.sh
#!/bin/bash
echo "This is an error message" >&2
echo "This is normal output"
[root@kittod ~]# bash error02.sh
This is an error message
This is normal output

运行该脚本,但是结果看不出来有什么区别,默认情况下,Linux会将STDERR导向STDOUT,但是如果你在运行脚本时重定向了STDERR,脚本中所有导向到SDTERR的文本会被重定向。

[root@kittod ~]# bash error02.sh 2> error.log
This is normal output
[root@kittod ~]# cat error.log
This is an error message

6.2.2 永久重定向

如果脚本中有大量的数据需要重定向,那重定向每个echo语句就会很麻烦。取而代之,你可以用exec命令告诉shell在脚本执行期间重定向某个待定文件描述符。

[root@kittod ~]# cat error03.sh
#!/bin/bash
exec 1>testout.log
echo "This is an error message"
echo "This is normal output"
[root@kittod ~]# bash error03.sh
[root@kittod ~]# cat testout.log
This is an error message
This is normal output

exec 指令会启动一个新shell将STDOUT文件描述符重定向到文件,脚本中发给STDOUT的所有输出会被重定向到文件。

[root@kittod ~]# cat error04.sh
#!/bin/bash
exec 2>testerror04.log   #错误的会进入testerror04.log,正确的会在屏幕中打印出来
echo "first line"
echo "second line"
exec 1>testout04.log
echo "third line"
echo "4 line,this line should go to the testerror file" >&2  #会将原本的覆盖
[root@kittod ~]# bash error04.sh
first line
second line
[root@kittod ~]# cat testerror04.log
4 line,this line should go to the testerror file
[root@kittod ~]# cat testout04.log
third line

6.3 在脚本中重定向输入

你可以使用相同的方法将STDIN从键盘重定向到其他位置。exec命令允许你将STDIN重定向到Linux系统上的文件中。

exec 0<testfile

下面案例告诉shell应该从testfile中获得输入,而不是STDIN

[root@kittod ~]# cat testfile
This is the first line.
This is the second line.
This is the third line.
[root@kittod ~]# cat testin05.sh
#!/bin/bash
exec 0<testfile
count=1
while read line
do
    echo "Line #$count: $line"
    count=$[ $count + 1 ]
done
[root@kittod ~]# bash testin05.sh
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.

6.4 创建自己的重定向

在脚本中重定向输入和输出时,并不局限于这3个默认的文件描述符。在shell中最多可以有9个打开的文件描述符。其他6个从3~8的文件描述符均可用作输入或输出重定向。你可以将这些文件描述符中的任意一个分配给文件,然后在脚本中使用它们。

6.4.1 创建输出文件描述符

可以用exec命令来给输出分配文件描述符。和标准的文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配。这里有个在脚本中使用其他文件描述符的简单例子。

[root@kittod ~]# cat error05.sh
#!/bin/bash
exec 3>testout05.log
echo "This should display on the monitor"
echo "and this should be stored in the file" >&3
echo "Then this should be back on the monitor"
[root@kittod ~]# bash error05.sh
This should display on the monitor
Then this should be back on the monitor
[root@kittod ~]# cat testout05.log
and this should be stored in the file

这个脚本用exec命令将文件描述符3重定向到另一个文件。当脚本执行echo语句时,输出内容会像预想中那样显示在STDOUT上。但你重定向到文件描述符3的那行echo语句的输出却进入了另一个文件。这样你就可以在显示器上保持正常的输出,而将特定信息重定向到文件中(比

如日志文件)。也可以不用创建新文件,而是使用exec命令来将输出追加到现有文件中。

exec 3>>testout05.log

现在输出会被追加到testout05.log文件,而不是创建一个新文件。

6.4.2 重定向文件描述符

现在介绍怎么恢复已重定向的文件描述符。你可以分配另外一个文件描述符给标准文件描述符,反之亦然。这意味着你可以将STDOUT的原来位置重定向到另一个文件描述符,然后再利用该文件描述符重定向回STDOUT。

[root@kittod ~]# cat error06.sh
#!/bin/bash
exec 3>&1
exec 1>testout06.log
echo "This should store in the output file"
echo "along with this line" >&3
exec 1>&3
echo "Now things should be back to normal"
[root@kittod ~]# bash error06.sh
Now things should be back to normal
[root@kittod ~]# cat testout06.log
This should store in the output file
along with this line
更新后:
[root@kittod ~]# cat error06.sh
#!/bin/bash
exec 3>&1
exec 1>testout06.log
echo "This should store in the output file"
echo "along with this line" >&3
exec 1>&3
echo "Now things should be back to normal"
[root@server ~]# bash error06.sh
along with this line
Now things should be back to normal
[root@server ~]# cat testout06.log
This should store in the output file

首先,脚本将文件描述符3重定向到文件描述符1的当前位置,也就是STDOUT。这意味着任何发送给文件描述符3的输出都将出现在显示器上。第二个exec命令将STDOUT重定向到文件, shell现在会将发送给STDOUT的输出直接重定向到输出文件中。但是,文件描述符3仍然指向STDOUT原来的位置,也就是显示器。如果此时将输出数据发送给文件描述符3,它仍然会出现在显示器上,尽管STDOUT已经被重定向了。在向STDOUT(现在指向一个文件)发送一些输出之后,脚本将STDOUT重定向到文件描述符3的当前位置(现在仍然是显示器)。这意味着现在STDOUT又指向了它原来的位置:显示器。

6.4.3 创建输入文件描述符

可以用和重定向输出文件描述符同样的办法重定向输入文件描述符。在重定向到文件之前,先将STDIN文件描述符保存到另外一个文件描述符,然后在读取完文件之后再将STDIN恢复到它原来的位置。

[root@kittod ~]# cat testfile
This is the first line.
This is the second line.
This is the third line.
[root@kittod ~]# cat error07.sh
#!/bin/bash
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 "Googbye";;
    N|n) echo "Sorry,this is the end.";;
esac
[root@kittod ~]# bash error07.sh
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
Are you done now?y
Googbye
[root@kittod ~]# bash error07.sh
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
Are you done now?n
Sorry,this is the end.

在这个例子中,文件描述符6用来保存STDIN的位置。然后脚本将STDIN重定向到一个文件。read命令的所有输入都来自重定向后的STDIN (也就是输入文件)。在读取了所有行之后,脚本会将STDIN重定向到文件描述符6,从而将STDIN恢复到原先的位置。该脚本用了另外一个read命令来测试STDIN是否恢复正常了。这次它会等待键盘的输入。

6.4.4 创建读写文件描述符

尽管看起来可能会很奇怪,但是你也可以打开单个文件描述符来作为输入和输出。可以用同一个文件描述符对同一个文件进行读写。不过用这种方法时,你要特别小心。由于你是对同一个文件进行数据读写, shell会维护一个内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。

[root@kittod ~]# cat testfile
This is the first line.
This is the second line.
This is the third line.
[root@kittod ~]# cat error08.sh
#!/bin/bash
exec 3<> testfile
read line <&3
echo "read: $line"
echo "This is a test line" >&3
[root@kittod ~]# bash error08.sh
read: This is the first line.
[root@kittod ~]# cat testfile
This is the first line.
This is a test line
ine.
This is the third line.

这个例子用了exec命令将文件描述符3分配给文件testfile以进行文件读写。接下来,它通过分配好的文件描述符,使用read命令读取文件中的第一行,然后将这一行显示在STDOUT上。最后,它用echo语句将一行数据写入由同一个文件描述符打开的文件中。在运行脚本时,一开始还算正常。输出内容表明脚本读取了testfile文件中的第一行。但如果你在脚本运行完毕后,查看testfile文件内容的话,你会发现写入文件中的数据覆盖了已有的数据。当脚本向文件中写入数据时,它会从文件指针所处的位置开始。 read命令读取了第一行数据,所以它使得文件指针指向了第二行数据的第一个字符。在echo语句将数据输出到文件时,它会将数据放在文件指针的当前位置,覆盖了该位置的已有数据。

6.4.5 关闭文件描述符

如果你创建了新的输入或输出文件描述符, shell会在脚本退出时自动关闭它们。然而在有些情况下,你需要在脚本结束前手动关闭文件描述符。要关闭文件描述符,将它重定向到特殊符号&-。脚本中看起来如下:

exec 3>&-

该语句会关闭文件描述符3,不再在脚本中使用它。这里有个例子来说明当你尝试使用已关闭的文件描述符时会怎样。

[root@kittod ~]# cat baderror09.sh
#!/bin/bash
exec 3> testerror09.log
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3
[root@kittod ~]# bash baderror09.sh
baderror09.sh: line 9: 3: Bad file descriptor

一旦关闭了文件描述符,就不能在脚本中向它写入任何数据,否则shell会生成错误消息。在关闭文件描述符时还要注意另一件事。如果随后你在脚本中打开了同一个输出文件, shell会用一个新文件来替换已有文件。这意味着如果你输出数据,它就会覆盖已有文件。考虑下面这个问题的例子

[root@kittod ~]# cat error10.sh
#!/bin/bash
exec 3> test10.log
echo "This is a test line of data" >&3
exec 3>&-
cat test10.log
exec 3> test10.log
echo "This will be bad" >&3
[root@kittod ~]# bash error10.sh
This is a test line of data
[root@kittod ~]# cat test10.log
This will be bad

在向test10.log文件发送一个数据字符串并关闭该文件描述符之后,脚本用了cat命令来显示文件的内容。到目前为止,一切都还好。下一步,脚本重新打开了该输出文件并向它发送了另一个数据字符串。当显示该输出文件的内容时,你所能看到的只有第二个数据字符串。 shell覆盖了原来的输出文件。

6.5 列出打开的文件描述符

你能用的文件描述符只有9个,你可能会觉得这没什么复杂的。但有时要记住哪个文件描述符被重定向到了哪里很难。为了帮助你理清条理, bash shell提供了lsof命令。lsof命令会列出整个Linux系统打开的所有文件描述符。

该命令会产生大量的输出。它会显示当前Linux系统上打开的每个文件的有关信息。这包括后台运行的所有进程以及登录到系统的任何用户。有大量的命令行选项和参数可以用来帮助过滤lsof的输出。最常用的有-p和-d,前者允许指定进程ID( PID),后者允许指定要显示的文件描述符编号。要想知道进程的当前PID,可以用特殊环境变量$$( shell会将它设为当前PID)。 -a选项用来对其他两个选项的结果执行布尔AND运算,这会产生如下输出。

[root@kittod ~]# lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 11949 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 11949 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 11949 root 2u CHR 136,0 0t0 3 /dev/pts/0

如果没有lsof则安装一个

上例显示了当前进程( bash shell)的默认文件描述符( 0、 1和2)。 lsof的默认输出中有7列信息,如下表

与STDIN、 STDOUT和STDERR关联的文件类型是字符型。因为STDIN、 STDOUT和STDERR文件描述符都指向终端,所以输出文件的名称就是终端的设备名。所有3种标准文件都支持读和写(尽管向STDIN写数据以及从STDOUT读数据看起来有点奇怪)。现在看一下在打开了多个替代性文件描述符的脚本中使用lsof命令的结果。

[root@kittod ~]# cat error11.sh
#!/bin/bash
exec 3> test11.log1
exec 6> test11.log2
exec 7< testfile
lsof -a -p $$ -d 0,1,2,3,6,7
[root@kittod ~]# bash error11.sh
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 14063 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 14063 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 14063 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 14063 root 3w REG 253,0 0 67765927
/root/test11.log1
bash 14063 root 6w REG 253,0 0 67765933
/root/test11.log2
bash 14063 root 7r REG 253,0 73 68241722 /root/testfile

该脚本创建了3个替代性文件描述符,两个作为输出( 3和6),一个作为输入( 7)。在脚本运行lsof命令时,可以在输出中看到新的文件描述符。文件名显示了文件描述符所使用的文件的完整路径名。它将每个文件都显示成REG类型的,这说明它们是文件系统中的常规文件。

6.6 阻止命令输出

有时候,你可能不想显示脚本的输出。这在将脚本作为后台进程运行时很常见。如果在运行在后台的脚本出现错误消息, shell会通过电子邮件将它们发给进程的属主。这会很麻烦,尤其是当运行会生成很多烦琐的小错误的脚本时。要解决这个问题,可以将STDERR重定向到一个叫作null文件的特殊文件。 null文件跟它的名字很像,文件里什么都没有。 shell输出到null文件的任何数据都不会保存,全部都被丢掉了。

在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示。

[root@kittod ~]# ls -l > /dev/null
[root@kittod ~]# cat /dev/null

这是避免出现错误消息,也无需保存它们的一个常用方法。

[root@kittod ~]# ls -la hehe
ls: cannot access 'hehe': No such file or directory
[root@kittod ~]# ls -la hehe testfile 2> /dev/null
-rw-r--r--. 1 root root 73 May 5 13:16 testfile

也可以在输入重定向中将/dev/null作为输入文件。由于/dev/null文件不含有任何内容,通常用它来快速清除现有文件中的数据,而不用先删除文件再重新创建。

[root@kittod ~]# cat testfile
This is the first line.
This is a test line
ine.
This is the third line.
[root@kittod ~]# cat /dev/null > testfile
[root@kittod ~]# cat testfile

6.7 创建临时文件

Linux系统有特殊的目录,专供临时文件使用。 Linux使用/tmp目录来存放不需要永久保留的文件。大多数Linux发行版配置了系统在启动时自动删除/tmp目录的所有文件。系统上的任何用户账户都有权限在读写/tmp目录中的文件。这个特性为你提供了一种创建临时文件的简单方

法,而且还不用操心清理工作。

有个特殊命令可以用来创建临时文件。 mktemp命令可以在/tmp目录中创建一个唯一的临时文件。 shell会创建这个文件,但不用默认的umask值。它会将文件的读和写权限分配给文件的属主,并将你设成文件的属主。一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然, root用户除外)。

6.7.1 创建本地临时文件

默认情况下, mktemp会在本地目录中创建一个文件。要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了。

[root@server ~]# mktemp testing.XXXXXX
testing.umwbiR
[root@server ~]# ll testing*
-rw------- 1 root root 0  1月 24 05:37 testing.umwbiR

注意:X必须是大写的

mktemp命令会用6个字符码替换这6个X,从而保证文件名在目录中是唯一的。你可以创建多个临时文件,它可以保证每个文件都是唯一的。

[root@kittod ~]# cat test12.sh
#!/bin/bash
tempfile=$(mktemp test12.XXXXXX)

exec 3>$tempfile

echo "This script writes to temp file $tempfile"

echo "This is a the first line" >&3
echo "This is a the second line" >&3
echo "This is a the last line" >&3

exec 3>&-

echo "Done creating temp file.The contents are: "

cat $tempfile

rm -rf $tempfile 2> /dev/null
[root@kittod ~]# bash test12.sh
This script writes to temp file test12.MHO2Q5
Done creating temp file.The contents are:
This is a the first line
This is a the second line
This is a the last line
[root@kittod ~]# ll test12*
-rw-r--r--. 1 root root 331 May 6 13:13 test12.sh

这个脚本用mktemp命令来创建临时文件并将文件名赋给$tempfile变量。接着将这个临时文件作为文件描述符3的输出重定向文件。在将临时文件名显示在STDOUT之后,向临时文件中写入了几行文本,然后关闭了文件描述符。最后,显示出临时文件的内容,并用rm命令将其删除。

6.7.2 在/tmp创建临时文件

-t 选项会强制mktemp命令来在系统的临时目录来创建该文件。在用这个特性时, mktemp命令会返回用来创建临时文件的全路径,而不是只有文件名。

[root@kittod ~]# mktemp -t test13.XXXXXX
/tmp/test13.BZznn5
[root@kittod ~]# ll /tmp/test13*
-rw-------. 1 root root 0 May 6 13:15 /tmp/test13.BZznn5

只写命令会在tmp目录创建,-t会在临时目录创建

由于mktemp命令返回了全路径名,你可以在Linux系统上的任何目录下引用该临时文件,不管临时目录在哪里。

[root@kittod ~]# cat test13.sh
#!/bin/bash
tempfile=$(mktemp -t tmp.XXXXXX)
echo "This is a test file." > $tempfile
echo "This is the second line of the test." >> $tempfile
echo "The temp file is located at: $tempfile"
cat $tempfile
rm -f $tempfile
[root@kittod ~]# bash test13.sh
The temp file is located at: /tmp/tmp.7Qhf4T
This is a test file.
This is the second line of the test.
[root@kittod ~]# ll /tmp/tmp*
ls: cannot access '/tmp/tmp*': No such file or directory

在mktemp创建临时文件时,它会将全路径名返回给变量。这样你就能在任何命令中使用该值来引用临时文件了。

6.7.3 创建临时目录

-d 选项告诉mktemp命令来创建一个临时目录而不是临时文件。这样你就能用该目录进行任何需要的操作了,比如创建其他的临时文件。

[root@kittod ~]# cat test14.sh
#!/bin/bash
tempdir=$(mktemp -d dir.XXXXXX)
cd $tempdir
tempfile1=$(mktemp temp.XXXXXX)
tempfile2=$(mktemp temp.XXXXXX)
exec 7> $tempfile1
exec 8> $tempfile2
echo "Sending data to directory $tempdir"
echo "this is a test line of data for $tempfile1" >&7
echo "this is a test line of data for $tempfile2" >&8
[root@kittod ~]# bash test14.sh
Sending data to directory dir.07pqwq
[root@kittod ~]# ll dir.07pqwq/
total 8
-rw-------. 1 root root 44 May 6 15:08 temp.nEults
-rw-------. 1 root root 44 May 6 15:08 temp.ypDroV
[root@kittod ~]# cat dir.07pqwq/temp.nEults
this is a test line of data for temp.nEults
[root@kittod ~]# cat dir.07pqwq/temp.ypDroV
this is a test line of data for temp.ypDroV

这段脚本在当前目录创建了一个目录,然后它用cd命令进入该目录,并创建了两个临时文件。之后这两个临时文件被分配给文件描述符,用来存储脚本的输出。

6.8 记录消息

将输出同时发送到显示器和日志文件,这种做法有时候能够派上用场。你不用将输出重定向两次,只要用特殊的tee命令就行。tee命令相当于管道的一个T型接头。它将从STDIN过来的数据同时发往两处。一处是STDOUT,另一处是tee命令行所指定的文件名:

tee filename

由于tee会重定向来自STDIN的数据,你可以用它配合管道命令来重定向命令输出。

[root@kittod ~]# date | tee testfile
Thu May 6 15:14:15 CST 2021
[root@kittod ~]# cat testfile
Thu May 6 15:14:15 CST 2021

输出出现在了STDOUT中,同时也写入了指定的文件中。注意,默认情况下, tee命令会在每次使用时覆盖输出文件内容。

[root@kittod ~]# who | tee testfile
root tty1 2021-05-06 13:02
root pts/0 2021-05-06 13:03 (192.168.132.1)
[root@kittod ~]# cat testfile
root tty1 2021-05-06 13:02
root pts/0 2021-05-06 13:03 (192.168.132.1)

如果你想将数据追加到文件中,必须用-a选项,否则是覆盖。

[root@kittod ~]# date | tee -a testfile
Thu May 6 15:19:06 CST 2021
[root@kittod ~]# cat testfile
root tty1 2021-05-06 13:02
root pts/0 2021-05-06 13:03 (192.168.132.1)
Thu May 6 15:19:06 CST 2021

利用这个方法,既能将数据保存在文件中,也能将数据显示在屏幕上。

[root@kittod ~]# cat test15.sh
#!/bin/bash

tempfile=test15file.log

echo "This is the start of the test" | tee $tempfile
echo "This is the second line of the test" | tee -a $tempfile
echo "This is the end of the test" | tee -a $tempfile
[root@kittod ~]# bash test15.sh
This is the start of the test
This is the second line of the test
This is the end of the test 
[root@kittod ~]# cat test15file.log
This is the start of the test
This is the second line of the test
This is the end of the test

现在你就可以在为用户显示输出的同时再永久保存一份输出内容了。

6.9 实例

文件重定向常见于脚本需要读入文件和输出文件时。这个样例脚本两件事都做了。它读取.csv格式的数据文件,输出SQL INSERT语句来将数据插入数据库。

shell脚本使用命令行参数指定待读取的.csv文件。 .csv格式用于从电子表格中导出数据,所以你可以把数据库数据放入电子表格中,把电子表格保存成.csv格式,读取文件,然后创建INSERT语句将数据插入MySQL数据库。

[root@kittod ~]# cat test16.sh
#!/bin/bash

outfile='members.sql'
IFS=','

while read lname fname address city state zip
do
    cat >> $outfile << EOF	#EOF把后面的写入,遇到EOF时结束
     INSERT INTO members (lname,fname,address,city,state,zip) VALUES('$lname','$fname','$address','$city','$state','$zip');
EOF
done < ${1}

这个脚本很短小,这都要感谢有了文件重定向!脚本中出现了三处重定向操作。 while循环使用read语句从数据文件中读取文本。

当运行程序test16.sh时, $1代表第一个命令行参数。它指明了待读取数据的文件。 read语句会使用IFS字符解析读入的文本,我们在这里将IFS指定为逗号。脚本中另外两处重定向操作出现在同一条语句中:

cat >> $outfile << EOF

这条语句中包含一个输出追加重定向(双大于号)和一个输入追加重定向(双小于号)。输出重定向将cat命令的输出追加到由$outfile变量指定的文件中。 cat命令的输入不再取自标准输入,而是被重定向到脚本中存储的数据。 EOF符号标记了追加到文件中的数据的起止。

INSERT INTO members (lname,fname,address,city,state,zip) VALUES('$lname', '$fname', '$address', '$city', '$state', '$zip');

上面的文本生成了一个标准的SQL INSERT语句。注意,其中的数据会由变量来替换,变量中内容则是由read语句存入的。所以基本上while循环一次读取一行数据,将这些值放入INSERT语句模板中,然后将结果输出到输出文件中。在这个例子中,使用以下输入数据文件。

[root@kittod ~]# cat members.csv
Blum,Richard,123 Main St.,Chicago,IL,60601
Blum,Barbara,123 Main St.,Chicago,IL,60601
Bresnahan,Christine,456 Oak Ave.,Columbus,OH,43201
Bresnahan,Timothy,456 Oak Ave.,Columbus,OH,43201

运行脚本时,显示器上不会出现任何输出:

[root@kittod ~]# bash test16.sh members.csv 

但是在members.sql输出文件中,你会看到如下输出内容

[root@kittod ~]# cat members.sql
 INSERT INTO members (lname,fname,address,city,state,zip) VALUES('Blum','Richard','123 Main St.','Chicago','IL','60601');
 INSERT INTO members (lname,fname,address,city,state,zip) VALUES('Blum','Barbara','123 Main St.','Chicago','IL','60601');
 INSERT INTO members (lname,fname,address,city,state,zip) VALUES('Bresnahan','Christine','456 Oak Ave.','Columbus','OH','43201');
 INSERT INTO members (lname,fname,address,city,state,zip) VALUES('Bresnahan','Timothy','456 Oak Ave.','Columbus','OH','43201');

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/546074.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云全新云服务器实例S8/M8正式上线,性能提升高达115%!

2024年4月15日&#xff0c;腾讯云正式上线第八代云服务器标准型实例 S8和内存型实例M8。基于自研服务器的高密设计与硬件升级&#xff0c;搭载第五代英特尔至强可扩展处理器的腾讯云实例S8/M8&#xff0c;计算性能大幅提升&#xff0c;对比腾讯云云服务器上代实例&#xff0c;整…

Ubuntu 22.04安装中文输入法

1. 安装 sudo apt install fcitx5 2. 管理已安装的语言 Setting->Region & Language->Manage Installed Language 在下图中点击“安装”&#xff0c;之后需要等一会 选择Fcitx 5 3. 添加输入法 Setting->Keyboard 点击chinese 选择你想要的输入法 重启一下&a…

React + 项目(从基础到实战) -- 第七期

使用ant design 表单组件,开发登录,注册,搜索功能 React 表单组件 ,受控组件 案列 使用defaultVlue属性 bug : 改变了数据源,但是页面未重新渲染 {/* 表单组件 */}<button onClick{()>{console.log(text);}}>打印</button><button onClick{()>[setText(&…

【网络编程】Linux网络内核结构以及分布剖析

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之Linux网络内核结构以及分布剖析&#xff0c;在这篇文章中&#xff0c;你将会学习到在Linux内核中如何实现网络数据的输入和输出的&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助…

《TinyLlama: An Open-Source Small Language Model》全文翻译

【Title】 TinyLlama&#xff1a;开源小语言模型 【Abstract】 我们推出了 TinyLlama&#xff0c;这是一个紧凑的 1.1B 语言模型&#xff0c;在大约 1 万亿个令牌上进行了大约 3 个时期的预训练。 TinyLlama 基于 Llama 2&#xff08;Touvron 等人&#xff0c;2023b&#xff…

【Python】#3 基本程序控制

文章目录 一、分支结构if语句&#xff08;Python没有switch&#xff09;tips:紧凑形式&#xff1a;适用于简单表达式的二分支结构 二、循环结构1. for语句range函数 2. while语句3. 循环保留字&#xff1a;break/continue与带else的循环语句 一、分支结构if语句&#xff08;Pyt…

关于伴行天使车载监护器的技术路线

为了判断分析并反馈孩童是否昏睡状态&#xff0c;以预防因分心后排而导致的交通事故&#xff0c;本团队根据基于回归树对齐算法中获取的数据&#xff0c;建立了疲劳度评分机制。 本评分机制采用人脸关键点智能标注模型检测人脸&#xff0c;通过人脸识别68特征点检测、分别获取…

16.读取指定路径下的txt文档然后合并内容为一个txt文档。

1.题目要求 分别读取路径为 ./middle/phone/base/1_student_0.txt, ./middle/vr/base/1_teacher.txt, ./nearby/phone/base/1_student_0.txt, ./nearby/vr/base/1_teacher.txt, ./outside/phone/base/1_student_0.txt, ./outside/vr/base/1_teacher.txt 里面的文件&#xff…

【MATLAB源码-第43期】基于matlab的turbo码误码率仿真比较不同迭代次数,采用logmap/sova算法。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 Turbo码是一种前向纠错码 (Forward Error Correction, FEC)&#xff0c;在 1993 年由法国的两位研究员 Claude Berrou 和 Alain Glavieux 提出。这种编码技术以其接近 Shannon 极限的高性能而受到广泛关注。以下是关于 Turbo…

【word】文档标题如何自动编号

我在写一个word文档的时候&#xff0c;每一级标题的格式都设置好了&#xff0c;包括字体&#xff0c;大小等等&#xff0c;但是如何自动编号呢&#xff1f; 在写中期报告的时候&#xff0c;我对每一级标题的格式都创建了一个单独的样式&#xff0c;像这样&#xff1a; 对于每一…

Linux——信号量与基于环形队列的生产者消费者模型

目录 前言 一、信号量 二、信号量的接口 1.初始化 2.销毁 3.申请信号量 4. 释放信号量 三、基于环形队列的生产者消费者模型 1.环形队列的理解 2.生产者消费者的设计 3.单消费者单生产者环形队列的实现 4.多消费者多生产者环形队列的实现 前言 之前&#xff0c;…

AI的说服力如人类?Anthropic最新研究揭秘机器的辩论能力|TodayAI

人们常常对人工智能模型在对话中的说服力表现持怀疑态度。长久以来&#xff0c;社会上一直存在一个疑问&#xff1a;人工智能是否会达到人类那样&#xff0c;在对话中具有改变他人想法的能力&#xff1f; 直到最近&#xff0c;这一领域的实证研究相对有限&#xff0c;对于人工…

HTML5 新增语义标签及属性

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍HTML5 新增语义标签及属性&#x1f48e;1 HTML5 新增的块级语义化标签&…

海外代理IP在跨境电商中发挥什么作用?

在我国跨境电商的发展中&#xff0c;海外代理IP的应用日益广泛&#xff0c;它不仅帮助商家成功打入国际市场&#xff0c;还为他们在多变的全球电商竞争中保持优势。下面是海外代理IP在跨境电商中五个关键的应用场景。 1、精准的市场分析 了解目标市场的消费者行为、产品趋势以…

pyinstaller工具打包python项目详细教程

使用 Pyinstaller工具 编译打包 Python 项目生成 exe 可执行文件 1.pyinstaller介绍&#xff1a; 介绍&#xff1a;PyInstaller 是一个将 Python 程序转换为独立可执行文件的工具。它能够在 Windows、Linux、Mac OS X、AIX 和 Solaris 等多种系统上运行。详细介绍可参考pyins…

记录--病理切片图像处理

简介 数字病理切片&#xff0c;也称为全幻灯片成像&#xff08;Whole Slide Imaging&#xff0c;WSI&#xff09;或数字切片扫描&#xff0c;是将传统的玻片病理切片通过高分辨率扫描仪转换为数字图像的技术。这种技术对病理学领域具有革命性的意义&#xff0c;因为它允许病理…

Git分布式版本控制系统——在IDEA中使用Git(二)

四、IDEA中本地仓库的操作 1.将文件加入暂存区 2.将暂存区的文件提交到版本库(相当于git commit) 3.查看日志 五、IDEA中远程仓库的操作 1.查看远程仓库 2.添加远程仓库 3.推送至远程仓库 4.从远程仓库拉取

pyqt实现星三角减压启动

这个对于plc上实现是非常容易得。它本来就是逻辑控制器&#xff0c;如果用代码实现它&#xff0c;该怎么做呢&#xff1f;这个实现起来看似简单&#xff0c;实则是有不少坑的&#xff08;大神除外&#xff09;。我一直想用类来封装&#xff0c;让它继承QObject,为啥非要继承QOb…

为什么MySQL数据库超过2000万条数据,查询依然很快:B+树和数据页结构解析

MYSQL数据库单表建议最大2000万条数据&#xff0c;很多人都说如果超过了2000万条数据&#xff0c;性能就会下降的特别厉害。但是你实际上存储后&#xff0c;发现即使超过了2000万但是查询依旧很快&#xff0c;这是为什么&#xff1f; Mysql为了查询速度&#xff0c;内部使用了…

私域流量变现干货:轻松盘活,高效增长!

你知道如何增长私域流量并将这些流量转化为实际收益&#xff0c;让我们的品牌价值最大化吗&#xff1f; 今天&#xff0c;就分享几点干货&#xff0c;帮助大家盘活私域流量&#xff0c;实现高效增长&#xff01; 1、精准定位和用户画像 首先&#xff0c;了解您的私域流量源于…