[Linux Shell学习系列十一]脚本输入处理-2.重定向

D20

在Linux中,总有3个默认的设备文件是打开的,即标准输入stdin(键盘)、标准输出stdout(屏幕)、标准错误stderr(屏幕)。

这三个文件和任何打开的文件,都可以被重定向。

每个打开的文件被指定一个描述符,如stdin、stdout、stderr的文件描述符分别为0、1、2。对于打开的另外的文件,预留了文件描述符3~9。

 

1. 文件重定向

重定向默认只适用于一条命令。后面的命令继续发送到标准输出。

重定向发生在命令执行之前。

注意:之所以推荐使用cat命令读取文件内容,在于cat用于较好地把多个文件连接在一起,或作为Shell命令行提示符中查看文件内容的快速工具。在脚本中不应该使用cat命令来从管道文件到命令,而应该使用重定向。否则将导致额外的进程被创建。

使用标准输入:

#不指定任何参数而执行cat
$ cat
#似乎什么都不做也不显示命令行提示符,其实此时仍在从标准输入即终端进行读取

#此时输入aaa
aaa
aaa
#第一次显示:在标准模式下,终端回显你输入的字符并允许进行简易的编辑(如退格)等,直到按下回车,才把输入的内容发送到应用程序
#第二次显示:cat命令从标准输入读取行,然后显示到标准输出(同样是你的终端)。

#此时按下Ctrl+D:向终端发送文件结束符,此时cat认为标准输入已经关闭,cat停止读取并结束。
#Bash看到cat命令结束,显示Shell命令行提示符。
$

使用输入重定向将一个文件连接到标准输入:

$ cat < list.txt #此时cat命令为list.txt文件打开了一个文件描述符,并通过这个文件描述符读取文件的内容。此时cat的数据源是文件而不是标准输入。
ab dde
ab   dde
abde
333
a

 

重定向采用数字作为先导,数字表示了将要变更的文件描述符。

$ find . -name "a.txt" -exec lsattr {} \; #test和test-bak文件夹下的a.txt只有root用户可以删除
----i--------e-- ./test-bak/a.txt
----i--------e-- ./test/a.txt

$ cat rmtxt.sh 
#!/bin/bash
#202006

if [ $# -lt 1 ]
then
        echo "Usage: `basename $0` DIRECTORY..."
        exit
fi

for dir in $@
do
        find $dir -name "a.txt" -exec rm -f {} \; #找到文件进行删除
done 2>> errors.log #将标准错误重定向到文件
#注意这里应用于done,会将重定向应用于循环内所有标准错误的输出,
#严格的说,是Bash在循环开始前打开了
errors.log文件并将标准错误指向它,当循环结束时关闭了它。运行在循环内的所有命令从Bash继承打开的文件描述符。
$ ./rmtxt.sh a.txt #执行脚本,标准错误不输出到屏幕 

$
cat errors.log #查看文件
rm: cannot remove './test-bak/a.txt': Operation not permitted
rm: cannot remove './test/a.txt': Operation not permitted
#注意:由于使用>,每次循环开始前都会清空文件,因此执行多次后也只有最后一次的

如果不想文件在每次循环之前被清空?

可以使用双重重定向操作符>>。>>将不会清空文件,只是添加新的内容到文件末尾。

 

注意:

1)当一个应用程序需要文件数据且它的创建是为从标准输入读取数据时,使用重定向是一个好主意。而将cat的输出管道到进程是糟糕的办法。

2)当设计一个从不同源头获取数据的应用程序时,最好让你的应用程序从标准输入读取数据。这样,用户就可以使用重定向来获取他想要的数据。

 

2. 从文件输入

D21

1)在脚本中对一个代码块使用重定向读取文件的内容。

$ cat readtwolines.sh 
#!/bin/bash
#202006

if [ $# -ne 1 ]
then
        echo "Usage: `basename $0` FILEPATH"
        exit
fi

file=$1

#大括号{}之间的代码是一个代码块
{
        read line1
        read line2
} < $file #这个代码块的标准输入指向文件

echo "First line: $line1"
echo "First line: $line2"

exit 0

$ ./readtwolines.sh list.txt 
First line: ab dde
First line: ab   dde

2)将循环语句和重定向结合使用来读取文件内容,并对每行进行特定的处理。

$ cat redirectedWhileloop.sh
#!/bin/bash
#202006

if [ $# -ne 1 ]
then
        echo "Usage: `basename $0` filename"
        exit
fi

filename=$1
count=0

while read LINE #while循环读取内容并存入变量
do
        let count++
        echo "$count $LINE"
done < $filename #将while的标准输入重定向到文件

echo -e "\nTotal $count lines read."

$ ./redirectedWhileloop.sh list.txt 
1 ab dde
2 ab   dde
3 
4 
5 abde
6 333
7 a

Total 7 lines read.

也可以使用until循环实现同样的功能:

$ cat redirecteduntilloop.sh 
#!/bin/bash
#202006

if [ $# -ne 1 ]
then
        echo "Usage: `basename $0` filename"
        exit
fi

filename=$1
count=0

until ! read LINE #与上面的唯一区别:采用until
do
        let count++
        echo "$count $LINE"
done < $filename

echo -e "\nTotal $count lines read."

exit 0

$ ./redirecteduntilloop.sh list.txt 
1 ab dde
2 ab   dde
3 
4 
5 abde
6 333
7 a

Total 7 lines read.

 

3. 从文本或字符串输入

Bash还有一种重定向类型是here-documents,操作符是<<MARKER。这个操作符指示Bash从标准输入读取输入的内容,直到读取到只包含MARKER的行为止。

语法:

$ command <<[-]MARKER
      Here Document
   MARKER

MARKER可以是任何单词,但要选择一个不会在你的数据集合中出现的单词。在第一个标志(<< MARKER)和第二个标志(MARKER)之间的所有行都被认为是命令的标准输入,且第二个标志必须独占一行。

$ tr [a-z] [A-Z] <<END #也可以写成<< END(<<和MARKER之间可以有空格)
> ONE Two three
> for fIve SIx
> END
ONE TWO THREE
FOR FIVE SIX

在<<后追加减号-,将会忽略行首的制表符,可以使Shell脚本中缩进的here-documents的值不会被改变。

$ cat heredocTr.sh
#!/bin/bash
#202006

tr a-z A-Z <<END
        one two three
        four five six
END

tr a-z A-Z <<-END #这里添加-,忽略行首的制表符
        one two three
        four five six
END

$ ./heredocTr.sh
        ONE TWO THREE
        FOUR FIVE SIX
ONE TWO THREE
FOUR FIVE SIX

 

默认情况下,Bash替换会在here-documents的内容上执行,即here-documents内部的变量和命令会被求值或运行。例如:

$ cat <<EOF
> Working dir is $PWD
> EOF
Working dir is /home/user1 #变量PWD求值

可以使用单引号或双引号括起定界符MARKER来使Bash替换失效:

$ cat <<"EOF"
Working dir is $PWD
EOF
Working dir is $PWD #没有求值

 

其实,here-documents最常用的功能还是用于向用户显示命令或脚本的用法信息。如:

$ usage(){ 
> cat <<-EOF
> usage: command [-x] [-v] [file ...]
> A short explanation of then operation goes here.
> It might be a few lines long, but shouldn't be excessive. 
#注意不应嵌入很大的数据块,且应该将逻辑(代码)和输入(数据)分离
> EOF
> }

$ usage
usage: command [-x] [-v] [file ...]
A short explanation of then operation goes here.
It might be a few lines long, but shouldn't be excessive.

 

here-strings是here-documents的一个变种,由操作符<<<和作为标准输入的字符串构成(被Shell作为一个整体处理的序列)。

here-strings是一个用于输入重定向的普通字符串,并不是一个特别的字符串。

语法:command <<<WORD

$ tr a-z A-Z <<< one #一个单词不需要引号
ONE

$ tr a-z A-Z <<<"one two three" #带有空格的字符串,则必须用双引号引用
ONE TWO THREE

$ str="one two three" #可以使用变量发送数据
$ tr a-z A-Z <<<$str
ONE TWO THREE

#可以接收多行字符串
$ tr a-z A-Z <<<"one two three 
> four five six"
ONE TWO THREE
FOUR FIVE SIX

here-strings使用比here-documents更方便,特别是发送变量内容(而不是文件)到grep或sed等过滤程序时。

 

4. 空文件创建

语法:>filename

操作符>重定向输出到一个文件。如果没有命令指定且文件filename不存在,Bash会创建一个空文件。

此处省略示例。

 

5. /dev/null丢弃不需要的输出

语法:

#重定向标准输出信息到/dev/null
command > /dev/null 

#重定向标准错误信息到/dev/null
command 2> /dev/null

#重定向标准输出和标准错误信息到/dev/null
command &> /dev/null
#或
command >& /dev/null
#或
command > /dev/null 2>&1

示例:

$ grep "^user1" /etc/passwd && echo "That account found." || echo "That account not found."
user1:x:1001:1001::/home/user1:/bin/bash
That account found.

$ grep "^user1" /etc/passwd > /dev/null && echo "That account found." || echo "That account not found."
That account found.

 

6. 标准错误重定向

D22

符号2>为标准错误重定向操作符。

$ find . -name "x.txt" -exec rm -f {} \;
rm: cannot remove './x.txt': Operation not permitted

$ find . -name "x.txt" -exec rm -f {} \; 2> error.log #标准错误重定向到error.log

$ cat error.log 
rm: cannot remove './x.txt': Operation not permitted

 

7. 标准输出重定向

符号>为标准输出重定向

$ awk -F: '{print $1}' /etc/passwd | sort > userlist.txt #将/etc/passwd中第一列用户名排序号写入userlist.txt

$ uname -a > hostinfo #将uname -a的信息写入hostinfo

# du -s /home/* | sort -rn > usage.txt #生成一个按大小排序的个账号home目录大小的列表

 

8. 标准错误和标准输出同时重定向

语法:

command &> filename
command >& filename
command > filename 2>&1
command 2>&1 > filename

示例:

$ sh -x example.sh &> debug.log #调试脚本时的输出信息到文件中

$ ( ./configure && make && make install ) > make.log 2>&1 #编译信息输出到文件中
#或
$ ( ./configure && make && make install ) 2>&1 > make.log 

 

9. 追加重定向输出

符号>>用于追加重定向输出。

语法:

command >> filename

示例:

$ ./example.sh >> data.txt #脚本输出追加到文件

$ cat file1 file2 >> file3 #两个文件的内容追加到另一个文件

$ ( ./configure && make && make install ) >> make.log 2>&1
#或
$ ( ./configure && make && make install ) 2>&1 >> make.log 

 

10. 在单命令行进行标准输入输出重定向

在一条命令行中完成标准输入和标准输出的重定向。

语法

command < input_file > output-file
#或
< input_file  command > output-file

示例:

$ cat list.txt 
ab dde
ab   dde
abde
333
a

$ tr a-z A-Z < list.txt > output_list.txt #输入:list.txt,输出output_list.txt

$ cat output_list.txt 
AB DDE
AB   DDE
ABDE
333
A

示例:用于解包一个rpm归档文件

$ cat derpm.sh 
#!/bin/bash
#202006

if [ $# -ne 1 ]
then
        echo "Usage: `basename $0` target-file"
        exit
fi

TEMPFILE=/tmp/$$.cpio #指定一个唯一的临时文件名,$$为脚本运行时的PID

rpm2cpio < $1 > $TEMPFILE #使用rpm2cpio命令,将输入的rpm归档文件转换为变量代表的cpio归档文件

cpio --make-directories -F $TEMPFILE -i #使用cpio命令,将变量代表的cpio归档文件进行解包

rm -f $TEMPFILE #删除cpio归档文件

exit 0

 

本节结束

 

posted @ 2020-06-01 15:01  workingdiary  阅读(496)  评论(0)    收藏  举报