0%

一些shell基础知识

写在前面

最开始接触到的命令行是windows的cmd,一次用它来查ip感觉很神奇。后来接触到Linux,vim,从起初的抵触不适应,到后来的得心应手。使用命令行,离计算机更近。这篇文章记录一下shell下面的一些细节。分为

  1. 标准输入和命令参数的区别
  2. 在后台运行的命令退出终端也就全部退出了
  3. 单引号和双引号表示字符串的区别
  4. 有的命令和sudo一起用就command not found

标准输入和参数的区别

最初遇到这个问题的时候是在shell下面使用管道操作,例如:find ./ -name "*.c" | xargs grep -rn zhw,这条命令的含义是:在当前路径下的所有.c文件中查找zhw关键字,注意这里在管道的右侧加了关键字xargs,表示将find找到的结果作为参数传递给grep,否则grep则是直接查找标准输入中的内容,加上xargs参数后则是查找文件中的内容。

标准输入就是编程语言中诸如scanf或者readline获取到的信息;而参数是指程序的main函数传入的args字符串数组。管道符和重定向符都是将数据作为程序的标准输入,下面这个示例:

  1. grep要搜索的文件作为参数是可选的,-r可以在当前路径下递归查找
  2. find不加xargs和直接grep效果是一样的,说明没有指定查找文件,而使用xargs则指定了查找文件作为参数传递给grep

这里多说一点:

通常使用find命令查找特定的文件,然后再这些文件中用grep去匹配关键字,但是如果find的结果是空,那么通过xargs方式就相当于没有给grep指明特定查找文件(它就会在当前目录递归),而通过-exec则表示对匹配的文件执行该参数所给出的shell命令,此时grep实际并没有执行,这才符合我们的预期结果。

上面两个小例子,理解问题的本质是多么的重要,基础打牢固,花哨的东西就随便玩了!
除了xargs可以读取数据作为命令参数外,通过$(cmd)形式也能达到同样的效果,例如:

$(cmd)读取cmd命令输出的数据作为参数。

最后说一下如何区分命令能够接受标准输入还是参数:如果命令能够让终端阻塞,说明该命令能接受标准输入,反之就是不接受。rm命令是不接受标准输入的。

后台运行程序

如果一些服务需要长时间运行,比如main函数里面有个while(1)的死循环,程序运行后命令行会阻塞。在命令后加上&后程序就可以在后台运行而不会阻塞命令行。底层的原理或者说这是怎么实现的呢?

每一个命令行终端都是一个shell进程,每当有命令需要执行是shell会fork一个子进程去执行命令,而shell进程会阻塞,等待子shell进程退出才重新响应。写一个很简单的demo

1
2
3
4
5
void main()                                                                                           
{
while(1)
{}
}

加上&后,只是让shell进程不再阻塞,可以继续响应你的新命令,但是当你关掉这个shell命令行中终端后,依附于它的所有子进程都会退出。如下:

但是如果(cmd &)这样来运行命令,则是将cmd挂到一个systemd系统守护进程下,这样即使当终端退出后,对于刚才的命令也没有影响。如下:

不过nohup cmd &的做法似乎常见一些,原理类似。此外还可以试试tmux这个小工具,如果利用晚上的时候在服务器编译大型项目,这玩意挺好用的,不用担心断网(终端被kill)。

单引号和双引号的区别

使用set -x命令,可以开启shell的命令回显。

对于单引号:所见即所得,会将单引号内的内容原样输出

对于双引号:把双引号内的内容输出出来;如果内容中有命令、变量等,会先把变量、命令解析出结果,然后在输出最终内容来。

不加引号:不会将含有空格的字符串视为一个整体输出, 如果内容中有命令、变量等,会先把变量、命令解析出结果,然后在输出最终内容来,如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号,一般连续的字符串,数字,路径等可以用。所以在写shell脚本的时候一定注意使用小括号和双引号哈!

示例如下:

sudo 找不到命令

有时候普通用户可以印的命令,加上sudo之后却报错command not found,原因在于这个命令仅存在与该用户的环境变量PATH中。解决方法是:

  1. 使用绝对路径调用命令
  2. 将命令添加到需要使用的用户的环境变量中
    同样的道理,root用户或其他用户的命令另外一些用户也可能找不到。

参考资料:
关于Linux shell你必须知道的