Shell命令(3)

26 7月

源码在这里Github(forked from shehaodan/full-shell-demo)

  • 重定向
  • 进程与子进程
  • 过滤器
  • 参照资料

重定向

输入重定向<:输入方向就是数据从哪里流向程序。数据默认从键盘流向程序,如果改变了它的方向,数据就从其它地方流入,这就是输入重定向。可以改成不再使用键盘作为命令输入的来源,而是使用文件作为命令的输入。

command <file          # 将file文件中的内容作为command的输入
command <<END          # 从标准输入(键盘)中读取数据,直到遇见分界符END才停止(分界符用户可以自定义)
command <file1 >file2  # 将file1作为command的输入,并将command的处理结果输出到file2

#!/bin/bash
while read str; do
    echo $str
done <demo.txt

输出重定向>:输出方向就是数据从程序流向哪里。数据默认从程序流向显示器,如果改变了它的方向,数据就流向其它地方,这就是输出重定向。可以改成不再输出到显示器上,而是输出到文件中。这样就可以把命令的结果保存起来,当我们需要的时候可以随时查询。

>代表的是覆盖,>>代表的是追加。

# 标准输出重定向
command >file    # 以覆盖的方式,把command的正确输出结果输出到file中
command >>file   # 以追加的方式,把command的正确输出结果输出到file中

# 标准错误输出重定向
command 2>file   # 以覆盖的方式,把command的错误信息输出到file中
command 2>>file  # 以追加的方式,把command的错误信息输出到file中

# 正确输出和错误信息同时保存
command >file 2>&1        # 以覆盖的方式,把正确输出和错误信息同时保存到同一个file中
command >>file 2>&1       # 以追加的方式,把正确输出和错误信息同时保存到同一个file中
command >file1 2>file2    # 以覆盖的方式,把正确的输出结果输出到file1中,把错误信息输出到file2中
command >>file1 2>>file2  # 以追加的方式,把正确的输出结果输出到file1中,把错误信息输出到file2中

# 【不推荐】以下这两种写法会导致file被打开两次,引起资源竞争,stdout和stderr会互相覆盖
command >file 2>file
command >>file 2>>file

#!/bin/bash
for str in "1" "2" "3" "4"
do
    echo $str >>demo.txt  # 将输入结果以追加的方式重定向到文件
done

输出重定向的完整写法其实是fd>file或者fd>>file,其中fd表示文件描述符,如果不写,默认为1(标准输出文件)。stdin,stdout,stderr默认都是打开的,在重定向的过程中,0、1、2 这三个文件描述符可以直接使用。如上例所示,默认为1可以不写,但当文件描述符为大于1时,比如2,就必须写上。

如果你既不想把命令的输出结果保存到文件,也不想把命令的输出结果显示到屏幕上,干扰命令的执行,那么可以把命令的所有结果重定向到 /dev/null 文件中。可以把 /dev/null 当成 Linux 系统的垃圾箱,任何放入垃圾箱的数据都会被丢弃,不能恢复。

ls -l &>/dev/null

输入输出重定向就是通过修改文件描述符和真实文件之间的文件指针实现的,>和<即文件描述符操作符。

输出:

n>filename:以输出的方式打开文件filename,并绑定到文件描述符n。n不写时默认为1(标准输出文件)

n>&m:用文件描述符m修改文件描述符n,或者说用文件描述符m的内容覆盖文件描述符n,结果就是n和m都代表了同一个文件,因为n和m的文件指针都指向了同一个文件。因为使用的是>,所以n和m只能用作命令的输出文件。n不写时默认为1(标准输出文件)

n>&-:关闭文件描述符n及其代表的文件。n不写时默认为1(标准输出文件)

&>filename:将正确输出结果和错误信息全部重定向到filename

输入:

n<filename:以输入的方式打开文件filename,并绑定到文件描述符n。n不写时默认为0(标准输入文件)

n<&m:类似于 n>&m,但是因为使用的是<,所以n和m只能用作命令的输入文件。n不写时默认为0(标准输入文件)

n<&-:关闭文件描述符n及其代表的文件。n不写时默认为0(标准输入文件)

输入和输出:

n<>filename:同时以输入和输出的方式打开文件filename,并绑定到文件描述符n,相当于n>filename和n<filename的总和。n不写时默认为0(标准输入文件)

command >file 2>&1   # 先执行1>file,让文件描述符1指向file
                     # 再执行2>&1,用文件描述符1修改文件描述符2,让2和1的内容一样
                     # 最终1和2都指向了同一个文件,也就是file。所以不管是向1还是向2中输出内容,最终都输出到file文件中
                  
echo "点评" 10>log.txt >&10  # 先执行10>log.txt,打开log.txt,并给它分配文件描述符10
                             # 接着执行>&10,用文件描述符10来修改文件描述符1,让1和10都指向log.txt文件
                             # 最终的结果是向log.txt文件中输出内容。
                          
echo "点评" 10>log.txt >&10 10>&-   # 文件描述符10只用了一次,在末尾最好将它关闭

exec命令可以永久性重定向,后续命令的输入输出方向也被确定了,直到再次遇到exec命令才会改变重定向的方向,即一次重定向,永久有效。

echo "重定向未发生"
exec >log.txt
echo "hello"
echo "world"
exec >&2
echo "重定向已恢复"
cat log.txt

# 重定向未发生
# 重定向已恢复
# hello
# world

代码块的重定向,就是由多条语句组成的一个整体;for、while、until 循环,或者 if…else、case…in 选择结构,或者由{ }包围的命令都可以称为代码块。将重定向命令放在代码块的结尾处,就可以对代码块中的所有命令实施重定向。

#!/bin/bash
sum=0
while read n; do
    ((sum += n))
done <nums.txt  # 输入重定向
echo "sum=$sum"

# 对{}包围的代码使用重定向。
{
    echo "lily";
    echo "7"
} >log.txt  # 输出重定向
{
    read name;
    read age
} <log.txt  # 输入重定向
echo "$name已经$age岁了"

进程与子进程

组命令就是将多个命令划分为一组,或者看成一个整体,在使用重定向和管道时会特别方便。

{ command1; command2; command3; ...; }  # 花括号{}和命令之间必须有一个空格,并且最后一个命令必须用一个分号或者一个换行符结束
(command1; command2; command3; ...;)    # 由小括号()包围起来的组命令会创建一个子Shell,所有命令都在子Shell中执行

ls -l > out.txt                         # >表示覆盖
echo "https://zxljack.com/" >> out.txt  # >>表示追加
cat readme.txt >> out.txt

{ ls -l; echo "https://zxljack.com/"; cat readme.txt; } > out.txt  # 简化为组命令

在子Shell中执行意味着,运行环境被复制给了一个新的shell进程,当这个子Shell退出时,新的进程也会被销毁,环境副本也会消失,所以在子Shell环境中的任何更改都会消失(包括给变量赋值)。因此,在大多数情况下,除非脚本要求一个子Shell,否则使用{}比使用()更受欢迎,并且{}的进行速度更快,占用的内存更少。

命令替换:把一个命令的输出结果赋值给另一个变量,例如dir_files=`ls -l`或date_time=$(date);

进程替换:把一个命令的输出结果传递给另一个(组)命令。

<(commands) # 产生标准输出,借助输入重定向,它的输出结果可以作为另一个命令的输入
>(commands) # 用来接受标准输入,借助输出重定向,它可以接收另一个命令的输出结果

# 例1:标准输出
read < <(echo "https://zxljack.com/")  # 第一个<表示输入重定向,第二个<()用来捕获输出结果,将输出结果重定向到read的输入
echo $REPLY                            # https://zxljack.com/

# 例2:标准输入
echo "Jack" > >(read; echo "你好,$REPLY")  # 你好,Jack

Linux管道和重定向的区别:

  1. 重定向操作符>将命令与文件连接起来,用文件来接收命令的输出。
  2. 管道符|将命令与命令连接起来,用第二个命令来接收第一个命令的输出。
  3. 管道和重定向可以混用

使用组命令、管道、命令替换,都是以新进程的方式运行Shell脚本

过滤器

过滤器通常与Linux管道一起使用,会获取输入,通过某种方式修改其内容,然后将其输出。

awk:用于文本处理的解释性程序设计语言,通常被作为数据提取和报告的工具。
cut:用于将每个输入文件(如果没有指定文件则为标准输入)的每行的指定部分输出到标准输出。
grep:用于搜索一个或多个文件中匹配指定模式的行。
tar:用于归档文件的应用程序。
head:用于读取文件的开头部分(默认是10行)。如果没有指定文件,则从标准输入读取。
paste:用于合并文件的行。
sed:用于过滤和转换文本的流编辑器。
sort:用于对文本文件的行进行排序。
split:用于将文件分割成块。
strings:用于打印文件中可打印的字符串。
tac:与cat命令的功能相反,用于倒序地显示文件或连接文件。
tail:用于显示文件的结尾部分。
tee:用于从标准输入读取内容并写入到标准输出和文件。
tr:用于转换或删除字符。
uniq:用于报告或忽略重复的行。
wc:用于打印文件中的总行数、单词数或字节数。

参照资料

常用linux命令行
shell编程100例

发表评论

电子邮件地址不会被公开。 必填项已用*标注