1. 标准输入和输出

1. 标准输入和输出

  • 程序:指令+数据
    读入数据:Input
    输出数据:Output
  • 打开的文件都有一个fd: file descriptor (文件描述符)
  • Linux给程序提供三种I/O设备
    • 标准输入(STDIN)-0 默认接受来自键盘的输入
    • 标准输出(STDOUT)-1 默认输出到终端窗口
    • 标准错误(STDERR)-2 默认输出到终端窗口
  • I/O重定向:改变默认位置

2. fd示例:

  1. 详细可以查看博客 https://zhuanlan.zhihu.com/p/105086274
  2. 首先用tail -f 持续打开一个文件,这时首先给tail程序创建一个进程,其中的文件描述符fd列表也创建成功;
  3. 然后在另一个终端上面进入 /etc/proc中找到它的 进程编号,可以用pidof tail来显示
  4. 然后进入到这个进程编号目录内,找到fd,查看其中的内容程序tail ctrl+c关闭之后 这个进程编号便会消失,同时fd自然也就没有了(fd是在进程编号里面的文件夹)
20:16[root@centos7 /data]# tail -f /data/1.t 2awda awdf###n dwa /naaa
  • 另一个终端:
bash 20:37[root@centos7 ~]# cd /proc 20:38[root@centos7 /proc]# ls 1 26 3018 6 6245 7698 8144 8329 cmdline modules 10 27 36 6103 6250 7791 8150 8335 consoles mounts 11 27378 37 6105 6292 7804 8163 8343 cpuinfo mpt 12 27438 38 6107 6586 7810 8164 8352 crypto mtrr 13 27526 39 6110 66 7814 8174 8354 devices net 14 27537 47 6111 667 7816 8182 8359 diskstats pagetypeinfo 14249 27551 4701 6135 6781 7820 8183 8372 dma partitions 14674 27707 4886 6136 6782 7832 8185 8380 driver sched_debug 16 27768 4888 6137 6785 7835 8187 8381 execdomains schedstat 16316 27847 4889 6138 6786 7910 8196 8390 fb scsi 1658 27890 4896 6141 6791 7915 8198 8391 filesystems self 1661 28 4897 6143 6810 8 8201 8405 fs slabinfo 1665 2886 49 6144 6811 8017 8202 8421 interrupts softirqs 1709 2889 4901 6150 6814 8031 8206 8427 iomem stat 1715 2890 4902 6160 682 8036 8208 8432 ioports swaps 1791 2898 4908 6161 687 8039 8221 8547 irq sys 1793 29 4910 6162 693 8066 8224 8566 kallsyms sysrq-trigger 18 2902 4914 6165 699 8085 8226 8572 kcore sysvipc 19 2905 4917 6170 7 8089 8227 8573 keys timer_list 2 2906 4918 6174 7197 8092 8229 8625 key-users timer_stats 20 2907 4919 6177 7209 8094 8232 8636 kmsg tty 21 2908 4920 6178 7229 8106 8236 9 kpagecount uptime 22 2909 4937 6180 7234 8110 8246 99 kpageflags version 23 2910 4941 6181 7248 8116 8248 acpi loadavg vmallocinfo 23591 2911 5 6183 7537 8123 8267 asound locks vmstat 24 2984 50 6185 7605 8124 8311 buddyinfo mdstat zoneinfo 25 3 51 6203 7612 8133 8314 bus meminfo 25713 3005 53 6235 7614 8135 8317 cgroups misc 20:38[root@centos7 /proc]# cd `pidof tail` 20:38[root@centos7 /proc/27526]# ls attr coredump_filter gid_map mountinfo oom_score sched statm autogroup cpuset io mounts oom_score_adj schedstat status auxv cwd limits mountstats pagemap sessionid syscall cgroup environ loginuid net patch_state setgroups task clear_refs exe map_files ns personality smaps timers cmdline fd maps numa_maps projid_map stack uid_map comm fdinfo mem oom_adj root stat wchan 20:38[root@centos7 /proc/27526]# cd fd 20:39[root@centos7 /proc/27526/fd]# ll total 0 lrwx------. 1 root root 64 Mar 11 20:39 0 -> /dev/pts/1 lrwx------. 1 root root 64 Mar 11 20:39 1 -> /dev/pts/1 lrwx------. 1 root root 64 Mar 11 20:39 2 -> /dev/pts/1 lr-x------. 1 root root 64 Mar 11 20:39 3 -> /data/1.t lr-x------. 1 root root 64 Mar 11 20:39 4 -> anon_inode:inotify

3. 标准输出重定向到其他文件或终端

  • STDOUT和STDERR可以被重定向到文件
    • 命令 操作符号 文件名
  • 支持的操作符号包括:
    • >把STDOUT重定向到文件
    • 2>把STDERR重定向到文件
    • &>把所有输出重定向到文件
  • ">"文件内容会被覆盖
    • set -C 禁止将内容覆盖已有文件,但可追加(Centos的bash默认设置)
    • >| file 强制覆盖(即使设置了-C也可以覆盖)
    • set +C 允许覆盖
  • ">>"原有内容基础上,追加内容

4. 标准错误重定向到其他文件或终端

  • 标准错误重定向的命令
    • 2> 覆盖重定向错误输出数据流
    • 2>> 追加重定向错误输出数据流
  • 标准输出和错误输出各自定向至不同位置
    • COMMAND > /path/to/file.out 2> /path/to/error.out
  • 合并标准输出和错误输出为同一个数据流进行重定向
    • &> 覆盖重定向
    • &>> 追加重定向
    • 举例:
      • COMMAND &> /path/to/file.out
    1. 另外一种写法重定向举例:
      • COMMAND > /path/to/file.out 2>&1 (顺序很重要,看下面解释)
      • COMMAND >> /path/to/file.out 2>&1
    • 注意:
      • 如果COMMAND 2>&1 > /path/to/file.out 这样写的话
      • 由于命令是由左往右执行,当执行到2>&1的时候,虽然把标准错误重定向到了标准输出,但此时标准输出还没有进行重定向,因此便仍然默认输出到了屏幕上面
      • 如果非要以这个顺序合并的话,那就( COMMAND 2>&1 ) > /path/to/file.out ,加上小括号变成一整体
      • 此时小括号其实是开了一个子shell,子shell是一个子进程,先把其中的错误输出放入标准输出中看做一个整体输出(这里参考fd相关知识,可知标准输出也是个文件描述符软链接f[1],指向屏幕,可以把它看做一个文件);
      • 然后回到父shell,再把子shell的整体标准输出f[1]再次重定向到文件中去,不让它在屏幕上输出,因此实现了这样的效果;
      • 这里就有点像博客中的管道效果
    1. 当然上面的也可以这样写,先把标准输出重定向到标准错误,然后再把标准错误进行重定向,效果是一样的,都是同时重定向到一个文件中去了:
      • COMMAND 2> /path/to/file.out 1>&2
      • COMMAND 2>> /path/to/file.out 1>&2
  • ():用小括号合并多个命令的STDOUT,或者同时合并错误和输出
    • 需要注意它和上面命令的区别,上面是一个命令,多个输出(标准错误和标准输出)进行合并重定向到一个文件
    • 而这里是多个命令一个标准输出(当然也可既包含标准错误和标准输出,用2>&1在括号内合并即可)同时合并到一个文件里面,无需分开合并或者追加重定向。
    • ( cal 2007 ; cal 2008 ) > all.txt
    • ( cal 2007 ; cal 0000 2>&1) > all.txt

注意点1:

  1. 可以利用重定向删除文件(先清空文件内容,这样就算被占用了也能把内容清空),然后再用rm 删除文件,看下面的第三项详细解释。
  2. 可以用> 指向 /dev/null 将标准输出不显示
  3. 可以用> 指向文件将文件内容清空 ,但注意有些shell可能不支持,比如csh,它只能用cat /dev/null > file 才可以
  4. 追加到文件中可以用>>
  5. 可以将两个命令用小括号括起来再使用
  6. 同时,正确和错误信息同时追加到一个文件也有一个写法2>&1, 放在括号里面,注意与后面的那好几个写法区分
  7. > 或者>> 也可以创建文件,但是因为>会把文件内容覆盖(假如存在想要创建的文件的同名文件), 所以用>>更加安全;
20:55[root@centos7 /data]# (hostname; uname -r) > 1.t 20:55[root@centos7 /data]# cat 1.t centos7.6test 3.10.0-957.el7.x86_64
21:36[root@centos7 /data]# (ls /eraf /home 2>&1) >1.t 21:38[root@centos7 /data]# cat 1.t ls: cannot access /eraf: No such file or directory /home: zhang
21:07[root@centos7 /data]# ls /eraf /home > 1.t 2>&1 21:08[root@centos7 /data]# cat 1.t ls: cannot access /eraf: No such file or directory /home: zhang 21:08[root@centos7 /data]# ls /eraf /home 2>1.t 1>&2 21:08[root@centos7 /data]# cat 1.t ls: cannot access /eraf: No such file or directory /home: zhang 21:09[root@centos7 /data]# ls /eraf /home 2>&1 >1.t ls: cannot access /eraf: No such file or directory 21:09[root@centos7 /data]# cat 1.t /home: zhang 21:12[root@centos7 /data]# ls /eraf /home &>1.t 21:12[root@centos7 /data]# cat 1.t ls: cannot access /eraf: No such file or directory /home: zhang

问题,其中那一项和其他选项结果不同

1. cmd &> file 2. cmd > file 2>&1 3. cmd 2>&1 > file # 此选项与其他选项结果不同 4. cmd 2>file 1>&2

问题:可以测试一下下面这几个命令的区别

ls /eraf /home 1>1.t 2>1.t ls /eraf /home 1>>1.t 2>1.t ls /eraf /home 2>>1.t 1>1.t ls /eraf /home 2>1.t 1>1.t ls /eraf /home 2>1.t 2>&1 1>1.t ls /eraf /home 2>>1.t 2>&1 1>1.t ls /eraf /home 2>&1 2>1.t 1>1.t ls /eraf /home 2>&1 2>>1.t 1>1.t

cat与标准输入

  1. cat 也有标准输入,只输入cat则会让你用键盘输入,按回车之后会再次显示出来,按ctrl+d可以离开
  2. cat可以键盘输入后输出到文件
# cat > file zhang duan # cat file zhang duan
  1. cat也可使用文件来代替键盘的输入,并同时输出到文件
cat < file1 > file2 # 其实这个和 cat file1 >file2 没什么区别
  1. 但是需要注意下面的两个结果
cat < file1 > file1 : 会把file1清0 cat < file1 >> file1 :会一直追加file1中的内容到file1下面,必须停止否则文件会过大 cat file1 >或>> file1 : 报错,不能这样用

5. tr转换—{tr,mail,seq}

  • tr 转换和删除字符
tr [OPTION]... SET1 [SET2] 选项: -c –C --complement 取字符集的补集 -d --delete 删除所有属于第一字符集的字符 -s --squeeze-repeats 把连续重复的字符以单独一个字符表示 -t --truncate-set1: 将第一个字符集对应字符转化为第二字符集对应的字符,忽略SET1多出来位数的字符(多出来的位数的字符不进行任何转化操作)
使用 - 符号表示一个范围,比如说 'a-z' 'A-Z' '0-9' ; 但注意数字的范围只能是个位数的范围,两位数以上的范围并不能进行替换; 下面是系统内定的转换字符: [:alnum:] 字母和数字 [:alpha:] 字母 [:cntrl:] 控制(非打印)字符 [:digit:] 数字 [:graph:] 图形字符 [:lower:] 小写字母 [:print:] 可打印字符 [:punct:] 标点符号 [:space:] 空白字符 [:upper:] 大写字母 [:xdigit:] 十六进制字符

注意点2:

  1. tr命令中的参数和选项的关系:

    • 选项中如果加入了-d(或者-dc)选项,则SET2序列这一个参数就不能添加了,这个参数就是把tr的标准输入中的SET1中的字符(或者补集)给删除掉。
    • tr命令中如果加入-s选项,则此时既可以单独只输入set1参数,也可以set1和set2参数都输入;此时会把tr命令的标准输入中的包含连续的set1和set2的字符的部分都给压缩成一个字符(注意set2的字符也压缩,也就是不论转换前SE1中的字符,还是转换后SET2中的字符,只要是属于它俩包含内的字符,都会被进行压缩;但是它俩之外的字符则不进行压缩),并且把set1字符转换为对应的set2字符;如果没有set2字符,则不进行转换只进行压缩。
    • tr的其他情况一般必须同时包含set1和set2两个参数
  2. tr命令默认会让你用键盘输入,退出的时候不能按回车(按回车只是换行,增加了一个\n),需要用ctrl+d快捷键

  3. tr转换中的字符串和通配符的区别在于:

    • tr转换里面不需要再加中括号了,直接代表一种字符
    • 而通配符整体仅仅代表一个字符,外层需要再加中括号才可以
ls [:lower:]* :显示出名称的第一个字符为:lower:中间的任意一个,后面接任意字符的文件或者目录(或者说是以:lower:任意一个字符开头的文件和文件夹) ls [[:lower:]]* : 显示出名称的第一个字符是小写字母,后面接任意字符的文件或者目录(或者说是以小写字母开头的文件和文件夹) tr '[:lower:][:upper:]' '12' :把标准输入的小写字母转换为1,大写字母转换为2;
  1. tr转换字符的时候:

    • 如果SET1比SET2中的字符数少,则SET1中少的位数的字符不进行任何操作;也就是只对SET1和SET2相同对应位数的字符进行转换操作。
    • 如果SET1比SET2中的字符数多,那么则默认把SET1中最后多出来的几位字符转换为SET2中的最后一位字符。
    • 第二种情况下,如果加上-t 选项,则SET1中最后多出来的位数的字符也不进行任何的转换操作了,和第一种情况类似。

    举例如下:

22:02[root@centos7 /data]# tr ‘abc’ ‘xyzmk’ abcdefaf xyzdefxf

22:04[root@centos7 /data]# tr ‘abcde’ ‘xyz’ abcderf xyzzzrf

22:06[root@centos7 /data]# tr -t ‘abcde’ ‘xyz’ abcedrf xyzedrf

```
  1. windows中的换行 包括两个字符,\r和\n;linux中只有一个\n 它会自动把光标移动到开头

    例如文件内容为: --------------- a b c 利用hexdump -C 查看文件 --------------- 在windows中: 00000000 61 0d 0a 62 0d 0a 63 |a..b..c| 00000007 在linux中: 0000000 61 0a 62 0a 63 0a |a.b.c.| 0000006

一些常用例子

1.利用tr计算 1+..100总和(两种方法)

09:45[root@centos7 ~]# echo {1..100} | tr ' ' '+' | bc 5050 09:46[root@centos7 ~]# seq -s + 100 |bc 5050
  • 附加知识点:需要标准输入的部分程序:bc ,cat ,tr,mail
    • 例如 bc < file1 file1中写有计算式,则屏幕上输出结果.

2.把/etc/issue中的小写字符都转换成大写字符

tr 'a-z' 'A-Z'< /etc/issue

3.删除fstab文件中的所有abc中任意字符

tr –d abc < /etc/fstab

4.只保留abc字符,其他的全部删除

tr -dc "abc" < /etc/fstab

例子里利用了标准输入和tr结合,以及管道

  • 使用 < 来重定向标准输入
  • 某些命令能够接受从文件中导入的STDIN
  • 利用管道进行上个命令标准输出到下个命令标准输入的fd改变,从而传递数据

6. 单行重定向和多行重定向

  • 使用“<<终止词”命令从键盘把多行重导向给STDIN
  • 直到 终止词 位置的所有文本都发送给STDIN
  • 多行重定向在bash中被称为就地文本(here Documents)
  • 例如
    • mail -s "Please Call" admin@magedu.com <<EOF

注意点3:

  1. 当使用多行重定向的时候,终止词最后必须单独一行且前后不能有任何字符包括空格等,否则不认为输入了终止词。(习惯用EOF)
  2. 单行重定向(也就是不写终止词的默认写法),每按一次回车,就会把结果重定向一次,而多行重定向只有终止词输入并回车之后才会把结果一次性输出,在此之前没有对文件做任何修改。例如:
# cat > file1.txt 123 # 第一行 abcde # 第二行 wfef # 第三行 1.其中每按一次回车就会进行一次重定向,比如输入123后,按一次回车就会把123输入到文件file1.txt中去; 2.以这样的方式把每一行的内容输入到file1.txt中去 3.最终文件中也是上面的三行内容; ------------------------------ # cat > file1.txt <<EOF > 123 > abcde > wfef > EOF 1.其中只有最后出现终止符然后再按回车之后,才会把终止符上面的内容全部一次性输入到文件file1.txt中去; 2.同样最终最终文件中也是上面的三行内容;
  1. 多行重定向的写法顺序没有要求,例如:
1. cat > file.txt <<EOF 2. cat <<EOF > file.txt # 1和2 写法没有区别,结果都一样 # 当然也可以追加重定向 3. cat >> file.txt <<EOF 4. cat <<EOF >> file.txt
  1. 多行重定向时,当终止符不包含引用符号的时候,则输入内容可以用“或者$()来输入命令等(进行参数扩展,命令替换和算数扩展,但是不进行路径扩展
    • 这种写法可以用在脚本中来输出各种不同的动态结果;当然也可以重定向输出结果到文件中去,不过下面的直接就输出在默认标准输出也就是屏幕上了,例如
# cat <<EOF > $PATH > echo $PATH > `echo $PATH` > echo \$PATH > kernal is `uname -r` > my hostname is `hostname` > ls * > $((1+2+3)) > echo $((1+2+3)) > `echo $((1+2+3))` > echo \$((1+2+3)) > my hostname is \`hostname\` > EOF 结果为: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #参数直接扩展,无需echo等,和下面几行对比 echo /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin #进行命令替换 echo $PATH #用\注释掉$符号 kernal is 3.10.0-1127.el7.x86_64 my hostname is localhost.localdomain ls * #不进行路径扩展(通配符) 6 #进行算数扩展,也不用echo这种,和下面几项对比 echo 6 6 echo $((1+2+3)) my hostname is `hostname` #用\注释掉$符号
  1. 但是多行重定向时,当终止符包含引用符号的时候,则输入内容不进行任何扩展,和4进行对比,例如
cat <<"EOF" > $PATH > echo $PATH > `echo $PATH` > echo \$PATH > kernal is `uname -r` > my hostname is `hostname` > $((1+2+3)) > echo $((1+2+3)) > `echo $((1+2+3))` > echo \$((1+2+3)) > my hostname is \`hostname\` > EOF 结果为 $PATH echo $PATH `echo $PATH` echo \$PATH kernal is `uname -r` my hostname is `hostname` $((1+2+3)) echo $((1+2+3)) `echo $((1+2+3))` echo \$((1+2+3)) my hostname is \`hostname\`
  1. 多行重定向符号 <<- 在脚本中更加常用,参考bash中的解释

7. 管道

  • 管道(使用符号|表示)用来连接命令
  • 命令1 | 命令2 | 命令3 | …
    • 将命令1的STDOUT发送给命令2的STDIN,命令2的STDOUT发送到命令3的STDIN
    • STDERR默认不能通过管道转发,可利用2>&1|&实现
    • 默认管道左右的每一个命令都会在subshell子shell中进行
    • 管道中的最后一个命令,和shopt中的lastpipe选项相关,如果它被设置并且没有jobs控制,则管道中的最后一个命令会在当前shell的前台环境中执行;默认它并没有被设置
    • 可用来组合多种工具的功能

一些例子:

  1. less :一页一页地查看输入
    ls -l /etc | less
  2. mail: 通过电子邮件发送输入
    echo "test email" | mail -s "test" wang@example.com
  3. bc:算术运算
    echo "2^3" | bc
  4. 转换大小写
    ls | tr ‘a-z’ ‘A-Z’

注意点4(常见标准输入命令):

  1. 管道前面命令必须要标准输出,管道后面命令必须要有标准输入命令.不需要标准输入的命令(比如echo需要的是参数,并不是标准输入)则无法正确使用管道
  2. 需要标准输入命令的有
    • cat
    • bc
    • mail
    • tr
  3. 易搞混不需要标准输入命令的
    • echo
    • ls

8. 管道重定向到多个目标—{tee}

命令使用方法:

Usage: tee [OPTION]... [FILE]... Copy standard input to each FILE, and also to standard output.

常用形式:

  • 命令1 | tee [-a ] [ 文件名 ] | 命令2
    • 把命令1的STDOUT保存在文件中,同时它也做为命令2的输入
    • -a 追加到文件中
  • 使用:
    • 保存不同阶段的输出
    • 复杂管道的故障排除
    • 同时查看和记录输出

注意点5:

  1. tee 后面只能跟文件(可以不加,但相当于什么都没做)
  2. tee 命令默认是覆盖到文件中,如果不想要覆盖而想要追加,则要加上-a的选项
  3. 用tee命令的时候注意,它相当于不仅把“命令1”的标准输出用>符号覆盖重定向到文件中去了,而且同时也把命令1的标准输出打印到默认的标准输出位置;如果后面还有管道符号,则由于管道的作用,也就是把命令1的标准输出传递给了命令2。

9. 管道特殊用法-

  • 示例:
    /home 里面的文件打包,但打包的数据不是记录到文件,而是传送到stdout,经过管道后,将 tar -cvf - /home 传送给后面的 tar -xvf - , 后面的这个 - 则是取前一个命令的 stdout,因此,就不需要使用临时file了

  • 操作命令:
    tar -cvf - /home | tar -xvf -

  • 本来的常规操作命令和步骤是:

tar -cvf 文件包名 /home 传输.... tar -xvf 文件包名
  • 将⽂件/etc/centos-release中每个单词(由字⺟组成)显⽰在独⽴⼀⾏,并⽆空⾏ 答案:cat /etc/centos-release |tr -cs ‘[:alpha:]’ ‘\n’

  • cat file > file.copy & ,后台执行命令输出file内容到file.copy

最后修改日期: 2021年7月7日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。