目录
I/O重定向
标准输入、输出和错误
标准输出重定向
标准错误重定向
重定向标准输出和错误到同一个文件
处理不需要的输出
标准输入重定向
管道线
过滤器
字符展开
路径名展开
波浪线展开
算术表达式展开
花括号展开
参数展开
命令替换
引用
双引号
单引号
转义字符
通过这个工具,可以重定向命令的输入输出,命令的输入来自文件,而输出也存到文件。 也可以把多个命令连接起来组成一个强大的命令管道。
cat — 连接文件
sort — 排序文本行
uniq — 报道或省略重复行
grep — 打印匹配行
wc — 打印文件中换行符,字,和字节个数
head — 输出文件第一部分
tail — 输出文件最后一部分
tee — 从标准输入读取数据,并同时写到标准输出和文件
标准输出通常由两种类型组成:
1、程序运行结果(程序要完成的功能)
2、程序运行状态和错误信息(程序进展)
“everything is a file”,程序实际是把运行的结果输送到一个叫做标准输出的特殊文件(通常用stdout表示),把状态信息输送到叫做标准错误的文件(stderr)。默认情况下,标准输出和标准错误都连接到屏幕,而不是保存到磁盘。另一方面,很多程序从叫做标准输入的文件中(stdin)得到输入,标准输入默认连接到键盘。I/O重定向允许我们更改输入来源和输出地点。
可以使用 “>” 重定向符号接文件名将标准输出输送到屏幕以外的其他文件。注意:使用 “>” 重定向操作符来重定向输出结果时,目标文件总是从开头被重写。所以每次使用重定向操作,目标文件原本的内容会被清空。
事实上,我们也可以使用重定向符号清空一个文件的内容(或者创建一个新的空文件):
> file_name
我们还可以使用 “>>” 重定向操作符把重定向内容追加到文件原来的内容后面(也可以用作创建新文件),而不是重写文件:
echo ‘I am a boy’ >> a.txt #追加单行文本
ls -l /usr/bin >> a.txt #将ls命令的结果(标准输出)追加到文件中
标准错误重定向没有专用的操作符,为了重定向标准错误,我们必须结合其文件描述符。一个程序可以在任一个经过编号的文件流上产生输出。我们将前三个编号的文件流称作标准输入、输出和错误,shell分别将其称为文件描述符0、1和2。即文件描述符2等同于标准错误,我们可以将文件描述符 “2” 与重定位符连在一起来重定向标准错误:
ls -l /bin/usr 2> a.txt #/bin/usr是一个不存在的目录,因此ls命令会输出标准错误
兼容旧版本shell的方法:
ls -l /bin/usr > a.txt 2>&1
首先重定向标准输出到文件a.txt,然后 重定向文件描述符2(标准错误)到文件描述符1(标准输出)使用表示法2>&1。注意重定向的顺序,标准错误的重定向必须总是出现在标准输出 重定向之后,要不然它不起作用。
现在的bash版本还提供第二种方法:
ls -l /bin/usr &> a.txt
我们使用一个&符号跟重定向操作符连接起来 “&>” 来重定向标准输出和错误到文件 a.txt。
有时候我们不想要一个命令的输出结果,例如错误和状态信息。系统中有一个叫做 “/dev/null” 的特殊文件,它是一个系统设备,叫做位存储桶,它可以接受输入但并不对输入做任何处理。我们可以将标准错误重定向到该文件中。
ls -l /bin/usr 2> /dev/null
先介绍一个能够用到标准输入的命令 cat,cat命令读取一个或多个文件,然后复制它们的内容到标准输出。可以使用cat不分页的显示文件内容。
cat file_name...
cat 经常被用来显示简短的文本文件。因为 cat 可以 接受不只一个文件作为参数,所以它也可以用来把文件连接在一起。比方说我们下载了一个 大型文件,这个文件被分离成多个部分(USENET 中的多媒体文件经常以这种方式分离), 我们想把它们连起来。如果文件命名为:
movie.mpeg.001 movie.mpeg.002 … movie.mpeg.099
我们可以使用通配符将它们连接起来,并重定向到一个文件中:
cat movie.mpeg.0* > movie.mpeg
目前为止,和标准输入还没什么关系。但是,如果我们不给cat输入参数直接运行,它就会从标准输入读入数据,而标准输入默认连接到键盘,所以当我们运行了不带参的cat命令的时候,它就会等待我们输入数据而不会显示任何内容。
当我们从键盘输入内容并按下enter键的时候,就可以看到屏幕上的文本行重复出现。
ldz@MSI:~$ cat
his cat is black.
his cat is black.
我们可以不断的输入内容,直到按下Ctrl+d告诉cat已经到达了文件末尾(EOF)。
我们可以利用cat结合重定向操作符创建简短的文本文件或代码文件,比如:
ldz@MSI:~$ cat > a.txt
hahahahahaha,打不过我吧。
哈哈哈哈哈,没有办法我就是这么强大。
注意结束输入的时候需要enter换行然后按下Ctrl+d,或者不换行连续使用两次Ctrl+d。
接下来,我们使用重定向操作符 “<” 来重定向标准输入:
cat < a.txt #将标准输入源从键盘改到文件a.txt
命令从标准输入读取数据并输送到标准输出的能力被一个称为管道线的 shell 特性所利用。 使用管道操作符 ”|”(竖杠),一个命令的标准输出可以通过管道送至另一个命令的标准输入(也属于I/O重定向):
command1 | command2
为了说明这个操作符,这里介绍一下less命令的另一个功能,除了浏览文件内容,它还可以接收标准输入并把其输送到标准输出并一页一页地显示。
ls -l /usr/bin | less
利用这个功能,我们可以非常方便的查看会产生标准输出的任一命令的运行结果。
管道线经常用来对数据完成复杂的操作。有可能会把几个命令放在一起组成一个管道线。 通常,以这种方式使用的命令被称为过滤器。
比如,把目录 /bin 和 /usr/bin 中的可执行程序放在一起并进行排序然后输出
ls /bin /usr/bin | sort | less
因为我们指定了两个目录(/bin 和 /usr/bin),ls 命令的输出结果由两个有序列表组成, 各自针对一个目录。通过在管道线中包含 sort,我们改变输出数据,从而产生一个有序列表。
uniq
uniq 命令经常和 sort 命令结合在一起使用。uniq 从标准输入或单个文件名参数接受数据有序 列表,默认情况下,从数据列表中删除重复行。(/bin 和 /usr/bin中有重名的程序 )
ls /bin /usr/bin | sort | uniq | less
如果想要输出重复行的数据,可以使用uniq的-d选项
ls /bin /usr/bin | sort | uniq -d | less
wc
wc命令是用来显示文件所包含的行数、字数(一个字被定义为由空格或换行字符分隔的字符串)和字节数。
wc a.txt
查看有序列表中的程序个数,-l 选项表示只输出行数
ls /bin /usr/bin | sort | uniq | wc -l
grep
grep 是个很强大的程序,用来找到文件中的匹配文本。
grep pattern file_name...
当 grep 遇到一个文件中的匹配”模式”(正则表达式),它会打印出包含这个类型的行。比如在程序列表中,找到文件名中包含单词”zip”的所有程序:
ls /bin /usr/bin | sort | uniq | grep zip
grep 有一些方便的选项:”-i”使得 grep 在执行搜索时忽略大小写(通常,搜索是大小写 敏感的),”-v”选项会告诉 grep 只打印不匹配的行。
head / tail
head 命令打印文件的前十行,而 tail 命令打印文件的后十行。默认情况下,两个命令都打印十行文本,但是可以通过”-n”选项来调整命令打印的行数。
head -n 5 a.txt
tail -n 5 a.txt
ls /usr/bin | tail -n 5
tail 有一个选项 “-f” 允许你实时地浏览文件。当观察日志文件的进展时很有用,因为它们同时在被写入。
tail -f logfile_name
使用”-f”选项,tail 命令持续监测这个文件,当新的内容添加到文件后,会立即出现在屏幕上。直到你输入 Ctrl+c。
tee
从标准输入读取数据,并且同时输出到标准输出(允许数据继续随着管道线流动)和一个或多个文件。主要用于在某个中间处理 阶段来捕捉管道线的内容。
ls /usr/bin | tee ls.txt | grep zip
再介绍一个新命令 echo,显示参数文本。(自动将参数排列成一行,超过一行的文本显示为单段落)
echo this is a test
传递到 echo 命令的任一个参数都会在(屏幕上)显示出来。
通配符所依赖的工作机制叫做路径名展开。eg:
echo D*
echo *s
echo [[:upper:]]*
echo /usr/*/share
隐藏文件路径名展开
echo * 不会显示隐藏文件。
echo .* 可以显示隐藏文件,但是结果中也会包括 "."(当前目录) 和 ".."(父目录) 这两个名字。(注意:多个圆点开头的文件名也属于隐藏文件。)
波浪线字符(“~”)用在 一个单词的开头时,它会展开成指定用户的家目录名,如果没有指定用户名,则展开成当前用户的家目录:
echo ~ username
shell 会在展开过程中执行算术表达式。所以我们可以把 shell 提示当作计算器来使用:
ldz@MSI:~$ echo $((2 + 2))
4
算术表达式展开使用这种格式:(美元符号加两层圆括号)
$((expression))
注意:算术表达式只支持整数
操作符 | 说明 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除(但是记住,因为展开只是支持整数除法,所以结果是整数。) |
% | 取余,只是简单的意味着,“余数” |
** | 取幂 |
算术表达式中空格并不重要,并且表达式可以嵌套。eg:
echo $(((5**2) * 3)) #也可以把每一层分别展开:echo $(($((5**2)) * 3))
算术表达式也可以嵌入文本中:
ldz@MSI:~$ echo Five divided by two equals $((5/2))
Five divided by two equals 2
通过花括号展开可以从一个包含花括号的模式中 创建多个文本字符串。eg:
ldz@MSI:~$ echo Front-{A,B,C}-Back
Front-A-Back Front-B-Back Front-C-Back
花括号展开模式可能包含一个开头部分叫做报头(如上例的Front-),一个结尾部分叫做附言(如上例的-Back)。花括号表达式本身可能包含一个由逗号分开的字符串列表,或者一个整数区间,或者单个的字符的区间。这种模式不能嵌入空白字符。
整数区间:
echo Number_{1..5}
倒序字母区间:
echo {Z..A}
花括号展开可以嵌套
ldz@MSI:~$ echo a{A{1,2},B{3,4}}b
aA1b aA2b aB3b aB4b
最常见的应用是,创建一系列的文件或目录列表。
mkdir {2007..2009}-0{1..9} {2007..2009}-{10..12}
这个特性在 shell 脚本中比直接在命令行中更有用。 它的许多功能和系统存储小块数据(变量),并给每块数据命名的能力有关系。简言之,就是变量值展开。格式为美元符号加变量名($variable)。
eg:”USER”变量中存放的是用户名
ldz@MSI:~$ echo $USER
ldz
如果是对未定义的变量进行参数展开会将其替换为一个空字符串。eg:数字1是没有定义的变量
ldz@MSI:~$ echo The total is $100.00
The total is 00.00
查看有效的变量列表
printenv | less
在其它展开类型中,如果你误输入一个模式,展开就不会发生。但在参数展开中,如果你拼写错了一个变量名, 展开仍然会进行,只是展开的结果是一个空字符串。
命令替换即把一个命令的输出作为一个展开模式来使用:
echo $(ls)
ls -l $(which cp)
这里我们把 which cp 的执行结果作为一个参数传递给 ls 命令,因此可以在不知道 cp 命令 完整路径名的情况下得到它的文件属性列表。
不只限制于简单命令,也可以使用整个管道线的输出作为展开模式。使管道线的输出结果成为 file 命令的参数列表。
file $(ls /usr/bin/* | grep zip)
bash中还支持旧版shell中的另一种语法,使用倒引号来替代美元符号和括号。
ls -l `which cp`
shell 提供了一种 叫做引用的机制,来有选择地禁止不需要的展开。
放在双引号中的文本都被当作普通字符来看待。但有几个例外:$、\ 和 ` `(倒引号)。也就是说,单词分割、路径名展开、 波浪线展开和花括号展开都将失效,但参数展开、算术展开和命令替换仍然执行。
所谓单词分割:Linux的单词分割机制先查找是否存在空格、制表符以及换行符,然后将它们作为单词间的界定符号,并全部替换成为单个空格。也就是说空格、制表符以及换行符都不会作为文本的一部分,而是作为分隔符使用,直接替换为单个空格(删除多余的空格)。Linux通过识别这些来将单词分割为不同的参数。eg:
ldz@MSI:~$ echo this is a test
this is a test
如果我们想要阻止单词分割,比如我们的文件名包含一个空格(即空格作为名称文本一部分),可以使用双引号来保留空格文本:
ls -l "two words.txt"
如果不使用双引号,系统会将文件名识别为两个参数:two 和 words.txt 。
注意,双引号中参数展开、算术表达式展开和命令替换仍然有效:
ldz@MSI:~$ echo "$USER $((2+2)) $(cal)"
ldz 4 September 2018
Su Mo Tu We Th Fr Sa
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
单词分割机制把换行符看作界定符,对命令替换产生了一个微妙但有趣的影响。
ldz@MSI:~$ echo $(cal)
September 2018 Su Mo Tu We Th Fr Sa 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
ldz@MSI:~$ echo "$(cal)"
September 2018
Su Mo Tu We Th Fr Sa
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
在第一个实例中,没有引用的命令替换由于单词分割的原因导致命令行包含38个参数。在第二个例子中, 命令行只有一个参数,参数中包括嵌入的空格和换行符。
单引号可以禁止所有的展开。
ldz@MSI:~$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
转义字符(即反斜杠 “\”)用来引用单个字符。通常在双引号中使用转义字符,来有选择地阻止展开。eg:
ldz@MSI:~$ echo "The balance for user $USER is: \$5.00"
The balance for user ldz is: $5.00
也常常使用转义字符来消除文件名中一个字符的特殊含义,如“$”, “!”, “&”, “ “(空格) 等字符。eg:创建一个名为"a a.txt"的文件
touch a\ a.txt
注意在单引号中,反斜杠也将失去它的特殊含义,被看作普通字符。
反斜杠转义字符序列
反斜杠除了作为转义字符外,也可以构成一种表示法,来代表某种特殊字符,这些特殊字符叫做控制码。ASCII 编码表中前32个字符被用来把命令转输到电报机之类的设备。如制表符、退格符、换行符、回车符等,还有大家不太熟悉的如空值、传输结束码、和确认。
常见的反斜杠转义字符序列:
转义序列 | 含义 |
\a | 响铃(”警告”-导致计算机嘟嘟响) |
\b | 退格符 |
\n | 新的一行。在类 Unix 系统中,产生换行。 |
\r | 回车符 |
\t | 制表符 |
echo 命令带上 ‘-e’ 选项,能够解释转义序列。可以把转义序列放在 $' ' (美元符号加单引号) 里面或者" "(双引号)里面。eg:
sleep 10; echo -e "Time's up\a" #十秒后计算机发出一声警告声
sleep 10; echo "Time's up" $'\a' #同上
echo -e "\a" #发出警告声
echo -e $'\a' #同上