概述
文章目录
- 开篇
- 前置命令和后置命令
- 异常(非零状态码)
- 状态码
- 输入输出重定向符
- a) 输出重定向 与 内联输出重定向的区别
- b) **++输入重定向++** 与 **++内联输入重定向++**
- 扩展 标准输入(0),标准输出(1),标准错误(2)
- c) 临时重定向 (>&)
- d) 永久重定向 (exec)
- 永久改变输出
- 永久改变输入
- exec 扩展
- 管道
- 参考资料
前置参考文章
Linux shell 整理之 基本概念篇(一)
Linux shell 整理之 基本概念篇(二)
Linux shell 整理之 用户权限篇(三)
开篇
如果读者看过了前三篇文章很容易发现,在我们使用命令的时候(不管是系统篇,权限篇),都是以单个命令执行程序的,而这篇是以多个命令组合、命令前后关联的角度来看待问题的。因此要牢牢抓住如下特性 异常,重定向,管道
。才能做到游刃有余。
在shell编程中,第一个特性,可能大家都没有注意到,shell编程中存在异常捕获的概念吗?有,当然有,但是与一般高级语言的异常处理不太一样。
前置命令和后置命令
本人特意指出这两个概念有一个核心目的,就是方便本文后面的概念的描述。否则太多的“前面的命令”,“后面的命令”,这样的字眼出现,显得有些笨拙冗余。
在复合命令中,在复合命令操作符(&&,||,<,>,<<,>>) 左端(前面)的命令叫做前置命令,后置命令就是复合命令操作符右端(后面)的命令。
异常(非零状态码)
状态码
首先要了解异常,必须要了解状态码,即命令最终的状态信息,一般正常的话为0
表11-2 Linux退出状态码
状 态 码 描 述
0 命令成功结束
1 一般性未知错误
2 不适合的shell命令
126 命令不可执行
127 没找到命令
128 无效的退出参数
128+x 与Linux信号x相关的严重错误
130 通过Ctrl+C终止的命令
255 正常范围之外的退出状态码
在脚本语言中,异常定义的公式如下
$ exit num ; # 其中 (num!=0)
即在脚本中任一段命令中抛出的状态码num 不等于 0 ,表示了抛出了异常,而不同于一般程序语言中的异常
特点(与高级语言对比)
- 不能捕获,也就是没有try…catch这样的语法,
- 一旦出现异常(exit num) 程序依然能够正确执行出错命令之后的命令
如果指定命令异常之后不想执行之后的逻辑命令,请使用特殊变量 ? ∗ ∗ 代 表 距 离 ∗ ∗ ?** 代表距离 ** ?∗∗代表距离∗∗? 命令所处位置的最近一次逻辑可执行命令执行之后的状态码,默认情况下,正常执行完成都会抛出0(代表正常退出)
#!/bin/bash
ldd; // ldd: missing file arguments
if [ $? -eq 0 ];then
echo "正常退出";
else
echo "异常退出"; // 执行该分支
exit 2; // 退出之后结束代码
fi
echo "asads" // 不执行
该代码中 ldd没有指定的命令,所以会抛出一个异常,因此会走分支2。在这里顺便提一句,在/bin/bash登陆shell的模式下,支持整数运算,不支持浮点值运算,这是一个非常巨大的缺陷,因此为了支持浮点运算,可以使用另一个非常高级的shell (zsh shell 佐罗/瑞士军刀).
在 zsh 中 扩展了bash shell 的很多功能,其最大的特性就是提供模块加载的功能,以内建命令的执行速度,执行自己扩展的功能。有兴趣的同学可以根据自己需要安装 zsh ubuntu zsh 官网
使用复合命令连接符(逻辑判断符)(&& 或则 ||)
可以使用 && ,使得在前置命令成功执行的情况下可以继续执行 && 的后置命令
原理也是简单,就是在判断 && 前置命令执行退出状态码为0(正常退出)的情况下能够继续执行后置命令。
可以使用 || ,作用于前置命令退出状态码非零的情况下,能够执行后置命令,当然默认shell执行环境下,不管前置命令是否正常退出,都会执行后置命令,但是 || 只有前后命令失败的情况下,才能执行后置命令
#!/bin/bash
ldd && echo "成功执行"
ls || echo "ls执行失败"
都会不执行后面的echo 命令
输入输出重定向符
< > << >>
-
> 覆盖写入,++输出重定向++
-
>> 追加写入 ++内联输出重定向++
-
< 回写,++输入重定向++
-
<< EOF(前置字符串可以为任一字符串) … EOF(必须更前置字符串一致) ++内联输入重定向++
-
>& 临时重定向 代表这前置输出被临时缓存到后置输出中,效果类似于>,不同的是,会清空前置输出,后置最后导流到后置输出,这个更像一个 ++管道++。也有人叫做 合并输出重定向
-
exec 永久重定向
a) 输出重定向 与 内联输出重定向的区别
homewell:~/shell1$ echo 1 > tail.logs
homewell:~/shell1$ cat tail.logs
1
homewell:~/shell1$ echo 2 > tail.logs
homewell:~/shell1$ cat tail.logs
2
homewell:~/shell1$ echo 3 >> tail.logs
homewell:~/shell1$ cat tail.logs
2
3
b) ++输入重定向++ 与 ++内联输入重定向++
其实,没有必要记住哪个符号是输入,哪个符号是输出,而是只要理解箭头地方向就是数据流动的方向,那么时间久了,也会自然而然想给这个方向做个定义,那么就会很自然地我们会 ++以前置命令作为主轴++,只要 ++流入++ 前置命令地方向就叫做 ++输入重定向++,从前置命令 ++流出++ 并向后置命令传入地就叫做 ++输出重定向++
程序接收输入
扩展 标准输入(0),标准输出(1),标准错误(2)
记忆技巧(性)把女人比作0,男人比作1,这样输入和输出的方向就很容易记了
在实际工作中,数据的输入和输出往往是经常使用的,比如日志输入到文件中,或者输入到控制台上。或者更高级一些,我们想把错误日志输出到控制台,而正常的日志输出到文件中(数据的分流,是不是很像过滤操作,亦或是导流)
文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
在linux系统中,内核把所有实例或者对象当作文件来处理
奇怪的现象
homewell:~/shell1$ ls -al badfile > test3
ls: cannot access 'badfile': No such file or directory
homewell:~/shell1$ cat test3
homewell:~/shell1$
错误信息只输出到了控制终端显示,并没有输出到文件中,这是一个很让人疑惑的地方。其实shell对错误信息的处理与普通输出是分开处理的。默认情况下也就是2号文件描述符与1号文件描述符默认指向的位置是一样的,都是发送到控制终端界面上的。然而2号描述符并不会随着重定向符(>)的方向而改变。
改变STDERR重定向到文件 (2>)
2> 字面含义,把2号文件描述符(标准错误信息)输出到后置命令中(文件名称。可以不存在)
homewell:~/shell1$ ls -al badfile 2> test3
homewell:~/shell1$ cat test3
ls: cannot access 'badfile': No such file or directory
同时把标准输出(1)与标准错误(2)导向同一个文件
1> 代表1号文件描述符所处的默认位置(控制终端)重新导向(覆盖)新地址
1>> 代表1号文件描述符所处的默认位置(控制终端)重新导向(追加)新地址
homewell:~/shell1$ ls -al test badtest test2 2> test5 1>> test5
homewell:~/shell1$ cat test5
ls: cannot access 'test': No such file or directory
ls: cannot access 'badtest': No such file or directory
test2:
total 8
drwxrwx--- 2 homewell homewell 4096 10月 9 09:35 .
drwxrwx--- 4 homewell homewell 4096 10月 9 09:38 ..
简写
(&> test6) == (2> test6 1>> test6)
& 可以理解为 所有输出符的合并(1+2+。。。)
homewell:~/shell1$ ls -al test badtest test2 &> test6
homewell:~/shell1$ cat test6
ls: cannot access 'test': No such file or directory
ls: cannot access 'badtest': No such file or directory
test2:
total 8
drwxrwx--- 2 homewell homewell 4096 10月 9 09:35 .
drwxrwx--- 4 homewell homewell 4096 10月 9 09:38 ..
c) 临时重定向 (>&)
这里>& 更像一个管道 把 &的前置输出与后置输出合并为后置输出
本人更习惯 1 (空格) >& (空格)2 这样,更能体会临时重定向作为操作符来使用,而不是把>&2 作为一个整体来记忆,这样我们就能活学活用,看如下事例
homewell:~/shell1$ cat error.sh
#!/bin/bash
echo "This is an error message" 1 >& 2
echo "This is nomal message"
homewell:~/shell1$ ./error.sh 2 > error #只把错误输出导入到error中
This is an error message 1
homewell:~/shell1$ cat error
This is nomal message
homewell:~/shell1$ ./error.sh 2 > error >& error2 # 把错误输出与标准输出都合并到error2中
homewell:~/shell1$ cat error
homewell:~/shell1$ cat error2
This is an error message 1
This is nomal message
d) 永久重定向 (exec)
永久的意思就是方向一旦确定,就无法再回头了,这个是比较麻烦的地方,也是头痛的地方
永久改变输出
homewell:~/shell1$ cat -n test11
1 #!/bin/bash
2 exec 2> test11err
3
4 echo "还没有执行exec永久重定向"
5
6 exec 1> test11out
7
8 echo "执行永久重定向并标准输出到test11out文件"
9
10 echo "执行永久重定向并标准错误到testerr中" >& 2
homewell:~/shell1$ ./test11 &> abc
homewell:~/shell1$ cat abc
还没有执行exec永久重定向
homewell:~/shell1$ cat test11err
执行永久重定向并标准错误到testerr中
homewell:~/shell1$ cat test11out
执行永久重定向并标准输出到test11out文件
永久改变输入
exec 0< testfile 代表在模块中接下来的程序默认输入永久改变为从testfile文件作为输入(这里暂时不讲while的语法,见后期的结构化命令篇)
homewell:~/shell1/linuxlearnging$ cat testfile
a
b
c
homewell:~/shell1/linuxlearnging$ cat input3.sh
#!/bin/bash
exec 0< testfile
while read line
do
echo "your content:" $line
done
homewell:~/shell1/linuxlearnging$ ./input3.sh
your content: a
your content: b
your content: c
exec 扩展
exec 不仅可以当永久重定向使用,也可以作为类似于(./ source)
作为执行者(executor),执行指定的命令和参数来替换当前shell进程
exec 命令行 参数
不管执行成不成功都不会继续往后执行,也就是直接替换了当前的shell进程的所有内容,并直接跳出(这个是最猛的宏替换)
homewell:~/shell1/linuxlearnging$ cat -n ./exectest.sh
1 #!/bin/bash
2
3 var1=zhangll
4 echo "$$"
5 echo "$BASHPID"
6 exec 0< testfile
7
8 echo "exec1 after var1: $var1"
9 echo "$BASHPID"
10
11
12 exec echo "exec $BASHPID"
13 echo "exec2 after var1: $var1"
homewell:~/shell1/linuxlearnging$ ./exectest.sh
14347
14347
exec1 after var1: zhangll
14347
exec 14347
homewell:~/shell1/linuxlearnging$ echo "$?"
0
管道
管道的理解是一个十分头疼的问题,本人刚刚接触的时候也只是以比较肤浅的观点去理解管道,普遍都有个共识,前一截管道(前置命令)的输出结果可以作为后一截命令(后置命令)的输入数据,然而往往忽略了一点,也就是这个过程是实时的,并不是说前置命令执行完之后才会执行后置命令,重点在于 前置命令一旦产生输出(往往前置命令的输出与代码结束式共存的,所以很难把握) 之后,系统内部会立马产生输出到后置命令作为输入。
理解这点我们需要一个工具辅助
当我们以tail命令作为输出的时候,tail命令是一直不停循环的,执行,根本没有停歇过。然后我们用| 命令却很好地把前置命令地输出作为后置命令地输出。
比如 只过滤一个拥有a地输出
管道符 |
# tail -f tail.logs | grep a
因此,当你在使用管道的时候,切记她是 ++流式的++
参考资料
《Linux命令行与shell脚本编程大全》
未完待续。。。
最后
以上就是顺心宝贝为你收集整理的Linux shell 整理之 复合命令行篇(四)开篇的全部内容,希望文章能够帮你解决Linux shell 整理之 复合命令行篇(四)开篇所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复