有关Linux命令行工具的一些趣事

在最开始学习向终端输入命令时总是感觉很神奇,终端究竟是怎样理解我的命令的?

终端如何加载进程

举个例子,当我输入cat test.txt之后,bash会通过一次fork复制出一模一样的子进程bash,然后再通过execve覆盖掉自己,重新加载新的程序cat,并把test.txt作为参数传递给cat。

这样子的来回反复,效率岂不是奇低?一个fork函数就不知道要费多少时间呢 ,更何况还要再重新加载另一个程序。

但是事实上,linux系统非常聪明,fork和execve都是通过内存映射来完成的,也就是说,当bash使用fork复制了一个一模一样的自己时,他其实并没有实际的拷贝,而只是通过映射,将新的子进程的所有代码区域、数据区域等都指向了自己。直到子进程需要修改某个变量的值时,才会真正的复制出来内容,而且只复制所修改的内容所在的页块这一小部分。也就是利用写时复制(CopyOnWrite)的惰性操作。

而execve稍微麻烦一些,需要先重置进程内容,再重新映射一遍各种区域到磁盘的相应位置,等到真正用到的时候在缓存到内存上来。总之二者都不会傻乎乎的浪费时间搬些没用的砖。

终端如何理解命令

别看终端支持的命令这么多、那么复杂,其实上,终端完成的绝大多数工作都是调用其他程序。偶尔会用一下内置的命令以及一些内置语法结构(function, if, while, &&, ||, >, <, |, &等等)。

在用户按下enter键输入命令后,终端先解析命令行,也就是通过空格将整个输入的字符串分割开来,成为一连串的单词,如果包含某些语法关键字,就进行相应的处理,比如| < > if && || while &等等。

举几个例子就明白了:

cat text.txt &:终端需要加载名为cat的程序,参数为test.txt,后台运行。

which which:终端需要加载名为which的程序,参数为which。which会搜索并输出名为which的文件的路径。

if ls; then echo 'exit normally'; fiif为关键字,当终端发现if时,会吞掉if,然后运行后边的ls程序,如果ls的返回值为0,也就是正常退出,则终端运行echo后面的内容,最后读到fi结束判断语句。

[ 1 = 1 ]:这句话真的很奇葩,我最开始一直以为[]是bash支持的关键字,表示判断,但实际上:输入which [之后,输出的结果是:/usr/bin/[,也就是说,[其实是一个程序,他的名字就是[,它读入后边的四个参数1 = 1 ],如果这几个参数的数学含义正确,就正常退出(返回值为0),否则就设置退出值为1,表示错误。然后再读入结尾的],如果没有读到结尾的],也会异常退出。最后由于[程序判断1 = 1正确,因此正常退出。这也解释了为什么必须要加空格,因为不加空格的话bash的分割就会出错。

这样子

[ 1 = 1 ] && echo ah!:前面的部分已经说过了,再说后面的。&&为终端的内置语法,具有短路(short-circuiting)的性质。a && b,a的正确并不能保证且的结果正确,因此要继续运行后面的部分,也就是b,判断b是否也正确。而如果a错误(也就是返回值非0),那么就不用在判断b了,此时b就不会被运行。

jobs:需要注意的是,jobs是内置命令,而非外部命令,因为job是bash自己内部保持的一个结构,其他外部命令是访问不了的。通过which jobs可以发现jobs是不存在的文件。

ls | grep '*.txt' | xargs cat:这个命令有点复杂,用到了管道操作,其实管道就是把前一个文件的输出定向到后一个文件的输入,也就是在fork后,execve前修改了各个进程的输入输出描述符。最后的xargs是一个程序,它读取cat作为自己的参数,并且用输入流的内容作为cat的参数调用cat。

实用的linux终端操作!

说了一大堆虚的东西,来点干货!

  1. 找到当前目录下(不包含子目录)的所有非.c源文件的文件并删除。

find -not -path './*/* -type f -not name '*.cpp' | xargs rm

  1. 编译c或cpp文件并运行

新建一个名为run的文件,输入以下内容:

#!/usr/bin/env bash
prefix=$(echo $1 | cut -d. -f1)
suffix=$(echo $1 | cut -d. -f1)
if [ $suffix = 'cpp' ]; then
g++ $1 -o $prfix && ./$prefix
elif [ $suffix = 'c' ]; then
gcc $1 -o $prfix && ./$prefix
fi

最后通过chmod a+x run赋予run运行权限,就可以在当前目录下输入./run test.cpp来代替g++ test.cpp -o test && ./test了。当然不change mode也是可以的,直接bash run test.cpp即可,不过要麻烦一些。

最后还能进一步把run加入到系统路径中,比如:

leehyukshuai@HES:~/code$ ./run test.cpp 
hello
leehyukshuai@HES:~/code$ sudo cp run /usr/bin/
[sudo] password for leehyukshuai: 
leehyukshuai@HES:~/code$ run test.cpp 
hello
leehyukshuai@HES:~/code$ 

一些解释:开头的#!shebang符,表示应该调用什么解释器解释该脚本文件。env是一个程序,会调用bash(仅是为了增加可移植性,并非必要)。

一些其他有趣的操作各位自己探索趴···

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值