shell的标准输入和输出

  无论是要交给程序处理的数据,还是控制脚本的简单命令,都少不了输入和输出。程序要做的第一件事就是处理如同一阴一阳的 “输入与输出”。


  
  1、从文件获取输入
  
  当我们希望向文件输出内容时,我们可以通过符号>或>>实现。而用代表输入重定向的符号<可以从文件中读取数据,如下:

  $ wc < my.file

  之所以选择这种形状的操作符号,原因在于它们可以从视觉上提示重定向的方向。
  
  很多shell命令可以接受一个或多个文件名作为参数,但如果没有给出文件名,命令就会从标准输入读取。使用这种命令时,可以采用command filename或者command

  2、将数据与脚本存放在一起
  
  <可以从文件读取数据,当你需要获得脚本输入,但又不想用单独的文件时,使用<<(here-document)从命令行而非文件重定向输入文本。如果放在shell脚本中,则脚本文件可以同时包含数据与代码。
  
  以下是名为ext.sh的shell脚本示例:

  # 下面是here-document
  
  grep $1 <

  当我们运行此脚本,可以传入一个参数,如下调用:

  $ ./ext.sh bill
  
  # 输出以下内容
  
  bill x.919

  grep命令查找第一个参数是否在指定文件中出现,如果没有指定文件,那么它会在标准输入中查找。通过设置here document,告诉shell将标准输入重定向(临时)到此处。<< 语法表示我们想创建一个临时输入源,EOF是一个任意的字符串(你想用什么都行),用作临时输入的终止符。它并不属于输入的一部分,只是作为标记告诉输入在哪里结束。
  
  3、避免here-document中的怪异行为
  
  here-document在使用时可能会出现一些怪异的行为。你想用上一节介绍的方法来保存一份简单的捐赠人列表,因此创建了一个名为donors.sh的文件,如下所示:

  grep $1 <

  但是运行时出现了奇怪的输出:

  $ ./donors.sh bill
  
  pete bill00
  
  bill $ 9
  
  $ ./donors.sh pete
  
  pete pete00

  正常情况下(除非使用了转义语法),bash手册页中是这样说的:“……here-document 的每一行都要执行参数扩展、命令替换以及算术扩展”。因此,最初的donors脚本中所发生的事情是捐赠额被当作shell变量了。例如,$100被视为shell变量$1,随后跟着两个 0。这就是为什么我们在搜索 “pete” 时,得到的是 pete00;搜索 “bill” 时,得到的是 bill00。
  
  解决办法:
  
  通过转义结尾标记中的任意或所有字符,修改脚本内容,关闭here-document内部的shell特性 (注意观察EOF位置的变化):

  grep $1 <<'EOF'
  
  pete $100
  
  joe $200
  
  sam $ 25
  
  bill $ 9
  
  EOF

  尽管其中存在非常微妙的区别,但也可以将<

  $ ./donors.sh pete
  
  pete $100

  4、获取用户输入
  
  输入不止从文件中获取,有时我们还需要获取用户输入的内容。此时,我们需要用到read命令,如下:

  $ read

  或者

  $ read -p "answer me this " ANSWER

  不带参数的read语句会读取用户输入并将其保存在shell变量REPLY中,这是read的最简形式。如果希望bash在读取用户输入前先输出提示信息,可以使用-p选项-p之后的单词就是提示信息,如果想提供多个单词,可以将其引用起来。记住,要在提示信息结尾处加上标点符号或空格,因为光标会停在那里等待输入-t选项可以设置超时值。指定秒数达到后,不管用户是否输入,read 语句都会返回。我们的示例同时用到了-t和-p选项,但你也可以单独使用-t选项。
  
  上面的方式获取用户输入时会以明文回显,那适用密码输入么?
  
  当我们需要用户输入敏感信息时,需要禁止用户输入内容的回显。此时用read命令读取用户输入,需要加上一个特殊选项来关闭回显:

  read -s -p "password: " PASSWD
  
  printf "%b" "\n"

  -s选项告诉read命令不要回显输入的字符(s代表silent),-p选项指明下一个参数是提示信息,会在读取用户输入之前显示。从用户那里读取到的输入行保存在变量 $PASSWD 中。在read之后,我们用printf 输出了一个换行符。这里的printf不能少,因为read -s会关闭字符回显。如果禁止了回显功能,当用户按下回车键时,就不会回显换行符,后续输出就会和提示信息出现在同一行。输出换行符会将光标带到下一行。
  
  当然,我们也可以选择一行,如下:

  read -s -p "password: " PASSWD ; printf "%b" "\n"

  Shell标准输出
  
  如果无法产生输出,那么软件也就没什么价值了,但长久以来,I/O一直是难缠的计算领域之一。问题是有太多类型的输出,向屏幕写入不同于向文件写入,向文件写入也不同于向磁带或闪存写入。所以,对于输出会产生一些问题,如下:
  
  ●软件开发人员是否要针对各种输出设备编写代码,甚至包括尚未发明的设备?
  
  ●写到哪个文件?程序怎么知道是该写入代表终端窗口的文件、磁盘文件还是其他种类的文件?
  
  显然,如果把这些事情都交给每个程序员是不合理的,所以这种事情留给 shell 就行了。
  
  1、输出到终端/终端窗口
  
  想要用shell命令产生一些简单的输出,使用内建命令echo。命令行中的所有参数都会打印到屏幕上。

  echo Please wait.

  输出:

  Please wait.

  结果和在bash提示符(字符$)后输入该命令相同:
  
  echo是最简单的bash命令之一。该命令可以将参数输出到屏幕上。但是有几点需要记住:
  
  ●首先,shell负责解析echo的命令行参数。将参数交给echo前,shell会完成所有的替换、通配符匹配等操作。
  
  ●其次,在解析参数时,参数之间的空白字符会被忽略,如下:
  
  shell对参数间的空白字符没有太多限制,这通常是一种不错的特性。但对于echo来说,就有点烦人了。
  
  ●保留输出中的空白字符。将字符放入引号中就可以保留空白字符,如下图:
  
  引号中的单词组成了echo命令的单个参数。该参数是一个字符串,shell不会干涉字符串的内容。实际上可以用单引号('')明确告诉shell不要干涉字符串。

  2、在输出中加入更多格式控制
  
  使用内建命令printf。例如:

  printf '%s = %d\n' Lines $LINES
  
  Lines = 24

  或者:

  $ printf '%-10.10s = %4.2f\n' 'Gigahertz' 1.92735
  
  Gigahertz = 1.93

  内建命令printf的行为 C语言中的同名库函数相似,其中第一个参数是格式控制字符串,之后的参数都根据格式规范(%)进行格式化。
  
  %和格式类型(本例为s或f)之间的数字提供了额外的格式化细节。
  
  对于浮点类型(f),第一个数字(指示符4.2 中的4)是整个字段的宽度。第二个数字(2)是应该在小数点右侧打印出的数位量。注意,结果会按照四舍五入处理。
  
  对于字符串,第一个数字是字段的最大宽度,第二个数字是要输出的字符数量。根据需要,字符串会被截断(长于max)或用空白填充(不足min)。如果指示符max和min 相同,那么就可以确保字符串按照该长度输出。指示符左侧的负号表示字符串向左对齐(在字段宽度内)。如果不使用负号,则字符串向右对齐
  
  3、消除输出中的换行符
  
  希望输出中不包含echo 默认生成的换行符。使用printf,做法很简单,去掉格式化字符串末尾的\n 即可,如下:

  printf "%s %s" next prompt

  如果是echo,则使用-n选项:

  $ echo -n prompt

  因为printf的格式字符串(第一个参数)末尾并没有换行符,所以命令行提示符($)出现在了printf的输出之后。该特性在shell脚本中用处更大,你可能希望在形成一整行前由多条语句逐部分输出,或者在读取输入前显示用户提示。
  
  换作echo命令消除换行符的方法有两种。
  
  首先,-n选项能够抑制输出行尾的换行符。
  
  另外,echo命令还可以处理多种具有特殊含义的转义序列(如表示换行符的\n),这些转移序列与C语言字符串中的类似。调用echo 命令时加上-e选项。其中一种转义序列是 \C,它并不会输出什么字符,而是禁止在行尾输出换行符。如下:

  $ echo -e 'hi\c

  4、保存命令输出
  
  如过想把命令输出保存在文件中,用>符号告诉shell将输出重定向至文件,例如:

  $ echo fill it up
  
  fill it up
  
  $ echo fill it up > file.txt

  我们来查看一下文件file.txt的内容,看看其中是否包含了命令的输出:

  $ cat file.txt
  
  fill it up

  示例第一部分的第一行中出现的echo命令包含了3个要输出的参数。第二行用 > 将这些输出保存到文件file.txt中,这就是看不
  
  到echo输出的原因。
  
  示例第二部分用cat命令显示文件内容。我们可以看出,文件中包含的正是echo本该输出的内容。
  
  cat命令得名自一个较长的单词concatenation(拼接)。该命令会将出现在命令行上的文件的输出拼接在一起,如果你输入cat
  
  file1、file2、file3,那么这些文件的内容会逐个发送到终端窗口。如果一个大文件被分成了两半,你也可以用cat将其恢复原样(也就是将两部分拼接起来),这只需将输出保存到另一个文件中:

  cat first.half second.half > mergeFile.txt

  5、丢弃输出
  
  你有时不想将输出保存到文件中或者有时甚至不想看到输出。如我们在查找某个文件时,忽略那些没有权限的提示,如下:
  
  此时,我们可以将输出重定向到/dev/null

  $ find / -name myfile 2> /dev/null

  其实,你可以将不想要的输出重定向到文件,然后再将其删除。但还有一个更简单的方法。Unix和Linux系统都存在一个特殊设备,该设备并非真实的硬件,而仅仅是一个位桶(bit bucket),我们可以将不需要的数据都扔进去。它就是/dev/null,非常适用于此类场景。写入其中的数据会被直接丢弃并不会占用磁盘空间,重定向很容易做到这一点。示例中,只有发往标准错误的输出被丢弃了

你可能感兴趣的:(shell)