tartarus's bolg tartarus's bolg
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • pdb

  • make

  • cmake

  • Linux and Unix

    • Linux Tool介绍
    • Useful Linux Tool
    • regular-expression
    • Basic-Shell-Script
    • Using-Structured-Commands
    • More-Structured-Commands
    • Handling-User-Input
    • Presenting-Data
      • Redirecting Output in Scripts
        • Temporarily redirecting each line
        • Permanent redirections
      • Redirecting Input in Scripts
      • Creating Your Own Redirection
        • Creating output file descriptors
        • Redirecting file descriptors
        • Creating input file descriptors
        • Creating a read/write file descriptor
        • Closing file descriptors
      • Suppressing Command Output
      • Using Temporary Files
      • Logging Messages
      • All in One Example
    • Script-Control
  • Basic_Software
  • Linux and Unix
tartarus
2023-05-29
目录

Presenting-Data

本章内容:

  • Redirecting Output in Scripts
  • Redirecting Input in Scripts
  • Creating Your Own Redirection
  • Suppressing Command Output
  • Using Temporary Files
  • Logging Messages

# Redirecting Output in Scripts

先来回顾一下重定向:

  • 分别将标准错误和标准输出重定向到不同的地方
$ ls
test1  test2
$ ls -al test1 badtest test2 badtest2
ls: cannot access 'badtest': No such file or directory
ls: cannot access 'badtest2': No such file or directory
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test1
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test2
$ ls -al test1 badtest test2 badtest2 2> error.log 1> normal.log
$ ls
error.log  normal.log  test1  test2
$ cat error.log
ls: cannot access 'badtest': No such file or directory
ls: cannot access 'badtest2': No such file or directory
$ cat normal.log
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test1
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 将标准错误和标准输出重定向到同一个地方 (标准错误有比标准输出更高的优先级,所以先打印标准错误)
$ ls -al test1 badtest test2 badtest2 &> all.log
$ cat all.log
ls: cannot access 'badtest': No such file or directory
ls: cannot access 'badtest2': No such file or directory
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test1
-rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test2
1
2
3
4
5
6

重定向到顺序不同,输出的结果也不同 (在 man bash 的 1290 行能看到):
(重定向到某种流时文件描述符前需要加上 & )

  • 将标准错误重定向到标准输出,然后将标准输出重定向到文件中

    $ ls
    test1  test2
    
    $ ls -al test1 badtest1 test2 badtest2 2>&1 1> log
    ls: cannot access 'badtest1': No such file or directory
    ls: cannot access 'badtest2': No such file or directory
    
    $ cat log
    -rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test1
    -rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    注意

    这说明 2>&1 会将标准错误流重定向到标准输出流然后进行输出到显示器上,然后标准输出流被重定向才输出到文件中。相当于每个流重定向操作都会有输出,然后才进行下一个流的重定向。

  • 将标准输出重定向到文件中输出,然后将标准错误重定向到标准

    $ ls
    test1  test2
    
    $ ls -al test1 badtest1 test2 badtest2 >log 2>&1
    
    $ cat log
    ls: cannot access 'badtest1': No such file or directory
    ls: cannot access 'badtest2': No such file or directory
    -rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test1
    -rw-rw-r-- 1 tartarus tartarus 0 May 28 23:50 test2
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    注意

    这说明 >log 会将标准输出流重定向到文件 log 中, 然后标准错误流被重定向到了标准输出流中,而此时标准输出流已经被重定向到了文件中。所以标准输出流和标准错误流都被重定向到了 log 文件中。等价于: ls -al test1 badtest1 test2 badtest2 &>log

在脚本中的输出重定向有两种方法:

  • Temporarily redirecting each line
  • Permanently redirecting all commands in the script

# Temporarily redirecting each line

如果需要在脚本中生成错误信息放到标准错误流中:

$ cat script.sh
#!/bin/bash 
# testing STDERR messages 
echo "This is an error">&2 
echo "This is normal output"

$ ./script.sh
This is an error
This is normal output

$ ./script.sh > normal.log 2> error.log
$ cat normal.log
This is normal output
$ cat error.log
This is an error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Permanent redirections

  • 使用 exec 将某一种流 (标准输出流或者标准错误流) 长期的重定向到文件或者另一种流中。
$ cat script.sh
#!/bin/bash 
# redirecting all output to a file 
exec >testout 

echo "This is a test of redirecting all output" 
echo "from a script to another file." 
echo "without having to redirect every individual line" 

$ ./script.sh

$ cat testout
This is a test of redirecting all output
from a script to another file.
without having to redirect every individual line
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 在脚本运行中间,也可以修改某一条命令的重定向:
$ cat script.sh
#!/bin/bash 
# redirecting all output to a file 
exec >testout 

echo "This is a test of redirecting all output" 
echo "from a script to another file.">&2 # 这一条命令的标准输出别重定向到了标准错误流,因为此时标准错误流没被重定向,所以会输出到🖥️上。
echo "without having to redirect every individual line" 

$ ./script.sh
from a script to another file.

$ cat testout
This is a test of redirecting all output
without having to redirect every individual line
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

但是着存在一个问题是,一旦使用了 exec 进行重定向,就很难将输出复原到原本的输出流上。
解决办法:见 Creating Your Own Redirection

# Redirecting Input in Scripts

使用输入重定向从文件中读取输入,比如在 read from file (opens new window) 中,我们就使用管道从文件中读取数据到 read 的输入流中,这里还有另一种方法可以实现:

$ cat log
This is the first line.
This is the second line.
This is the third line.

$ cat script.sh
#!/bin/bash 
# redirecting file input 
exec 0< log 
count=1 
while read line 
do 
  echo "Line #$count: $line" 
  count=$[ $count + 1 ] 
done
echo "Line Total: $count"%                                                                                            
$ ./script.sh
Line #1: This is the first line.
Line #2: This is the second line.
Line #3: This is the third line.
Line Total: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Creating Your Own Redirection

在 bash 中有 9 个 file descriptor (文件描述符,从 0 到 8),其中 0,1,2 为系统指定描述符,其余的描述符都可以人文指定。

# Creating output file descriptors

(用 script 记录 log 时非常有用)

使用方法:

  1. 重定向一个文件描述符到文件中
  2. 重定向命令的标准输出 / 标准错误输出到 1. 中指定的文件描述符

优点:
这样做,可以让没有进行输出重定向的命令的输出打印到显示器,重定向到自定的文件描述符的命令可以将结果输出到文件 (比如 log file) 中。

举例:

$ cat script.sh
#!/bin/bash 
# using an alternative file descriptor 
exec 3>log # 如果想要将运行结果添加到一个已经存在的文件中 exec 3>>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"

$ ./script.sh
This should display on the monitor
Then this should be back on the monitor

$ cat log
and this should be stored in the file
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Redirecting file descriptors

通过引入临时文件描述符,临时的重定向标准输出 / 标准错误输出到文件,然后之后又恢复他们默认的输出位置。

举例:将标准输出重定向到文件,执行一些命令后又重定向回显示器。

$ cat script.sh
#!/bin/bash 
# storing STDOUT, then coming back to it 
# 将文件描述符3重定向到标准输出,所有重定向到3的流都会在显示器上打印
exec 3>&1 
# 将标准输出流重定向到文件中,但是文件描述符3还是指向原来的位置,即显示器
exec 1>testout 

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"%                                                                           
$ ./script.sh
Now things should be back to normal
$ cat testout
This should store in the output file
along with this line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

(文件描述符 3 在上面 shell 中的作用类似于 C 语言中交换两个变量时使用的临时变量。)

# Creating input file descriptors

通过引入临时文件描述符,临时的将文件重定向到标准输入,然后之后又恢复标准输入从键盘输入。

$ cat script.sh
#!/bin/bash 
# redirecting input file descriptors 
# 将标准输入重定向到文件描述符6, 所有从键盘的输入都会到文件描述符6中
exec 6<&0 
# 将文件log重定性到标准输入
exec 0< log 
count=1 
# read从log读取输入
while read line 
do 
  echo "Line #$count: $line" 
  count=$[ $count + 1 ] 
done 

# 6重定向到标准输入,所有从键盘的输入又会回到标准输入中
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

$ ./script.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
Goodbye
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
27
28
29

# Creating a read/write file descriptor

创建一个可读可写的文件描述符:

$ cat script.sh
#!/bin/bash 
# testing input/output file descriptor 
# read data from testfile: 3< testfile
# write data to testfile: 3> testfile
exec 3<> testfile 
read line <&3 
echo "Read: $line" 
# 在读或者写如一个文件时,维护了一个指针指向当前读或者写的位置。
echo "This is a test line">&3

$ cat testfile
This is the first line.
This is the second line.
This is the third line.

$ ./script.sh
Read: This is the first line.

$ cat testfile
This is the first line.
This is a test line
ine.
This is the third line.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# Closing file descriptors

脚本运行结束后,文件描述符自动消除。但是,有时也需要在脚本结束前关闭文件描述符:
将文件描述符重定向到 $-

# 关闭问价描述符3
exec 3>&-
1
2

使用举例:

$ cat script.sh
#!/bin/bash 
# testing closing file descriptors 
exec 3> testfile 
echo "This is a test line of data">&3 
exec 3>&-
echo "This won't execute">&3

$ ./script.sh
./script.sh: line 6: 3: Bad file descriptor

$ cat testfile
This is a test line of data
1
2
3
4
5
6
7
8
9
10
11
12
13

# Suppressing Command Output

/dev/null
1

# Using Temporary Files

/tmp
1

在当前目录下创建一个临时文件:

# X表示产生的文件名后缀是随机的
mktemp filenameX..X
1
2

在 /tmp 文件夹下创建一个临时文件:

mktemp -t filenameX..X
1

使用举例:

$ cat script.sh
#!/bin/bash 
# creating a temp file in /tmp 
tempfile=$(mktemp -t tmp.XXXXXX) 
echo "This is a test file."> $tempfile

$ ./script.sh

$ cat tmp.bvjnZ4
This is a test file.
1
2
3
4
5
6
7
8
9
10
  • 在当前文件夹下创建一个临时文件夹
mktemp -d dir.XXXXXX
1
  • 在 /tmp 文件夹下创建一个临时文件夹
mktemp -t -d dir.XXXXXX
1

# Logging Messages

使用 tee 命令将同时输出到文件和屏幕。

$ cat script.sh
#!/bin/bash 
# using the tee command for logging 
tempfile=testfile 
echo "This is the start of the test" | tee $tempfile 
# -a: append to the given files, do not overwrite
echo "This is the second line of the test" | tee -a $tempfile 
echo "This is the end of the test" | tee -a $tempfile 

$ ./script.sh
This is the start of the test
This is the second line of the test
This is the end of the test
$ cat testfile
This is the start of the test
This is the second line of the test
This is the end of the test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# All in One Example

从命令行参数传入一个 csv 文件转化为 MySQL 格式:

❯ cat script.sh
#!/bin/bash 
# read file and create INSERT statements for MySQL 
echo "$1"

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
# 标识符EOF要放在行首,才能作为结束符
done < ${1}%                                                                                                          
❯ 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
❯ ./script.sh members.csv
members.csv
❯ 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'); 
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
27
28
29
30
31
32
33
34
35
36
上次更新: 12/27/2023, 8:55:47 AM
Handling-User-Input
Script-Control

← Handling-User-Input Script-Control→

Theme by Vdoing | Copyright © 2023-2023 tartarus | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式