Open abbshr opened 10 years ago
近期在学习Bash的时候读到了输入/输出和命令行处理部分,我本以为讲的不过是I/O重定向、文件读写、输入输出命令之类的东西。事实并非如此,被我忽视的命令行处理才是文章的精华所在。虽说我以前写过不少Shell脚本,也感觉那东西很简单好学,不过读完这部分我才发现我一直忽略了一个重要问题:“Shell是如何处理你输入的命令行?”。
现在网上的Shell教程很多,但深入学习的很少,即便有也鲜为人知,在此我将这部分重要的内容加上自己的理解记录下来,方便日后查看。
当你敲入一行代码并交给Shell执行时,命令行处理就开始发挥作用了。
shell从标准输入或脚本中读取的每行称为一个管道行,它包含一个或多个由0个或多个管道字符|分割的命令。shell会对其读取的每个管道行执行以下操作,也称作命令行处理:
|
这个过程可分为12个阶段:
空格、制表符、换行符、;、(、)、<、>、|、&
if
function
{
(
else
then
fi
~
~单词
$
$(string)
$((string))
$IFS
*
?
[
]
函数 > 内置命令 > $PATH里的脚本
这个过程复杂难记,我们可以挑选出容易搞混的几条,把他们的处理优先级记下来:
别名替换、大括号扩展、~扩展、变量替换、命令替换、算术替换、路径扩展、命令查找
~
上面个的12个步骤其实是可以被改变的,其中引号就可以忽略某些步骤。
'
"
命令查找顺序
shell默认的命令查找顺序是: 函数 内置命令 脚本 command
shell默认的命令查找顺序是:
而command可以忽略别名替换和函数查找:
command
cd () { command cd }
bulitin只会查找内置命令。
bulitin
enable能屏蔽内置命令:
enable
enable -n cd
内置命令eval的作用就是重新执行命令行处理,表面上看起来像在程序运行时执行动态生成的字符串。eval和JavaScript中的eval一样。
eval
一个使用eval的例子:
list="ls | more" $list # 一定会提示找不到那个文件,很明显,管道字符|在第六步才被解析出来, # 结果|和more都被当成ls的参数了。 # eval这里派上了用场 eval $list # 被解析的$list将重新进入命令行解析流程,因此会正确执行。
近期在学习Bash的时候读到了输入/输出和命令行处理部分,我本以为讲的不过是I/O重定向、文件读写、输入输出命令之类的东西。事实并非如此,被我忽视的命令行处理才是文章的精华所在。虽说我以前写过不少Shell脚本,也感觉那东西很简单好学,不过读完这部分我才发现我一直忽略了一个重要问题:“Shell是如何处理你输入的命令行?”。
现在网上的Shell教程很多,但深入学习的很少,即便有也鲜为人知,在此我将这部分重要的内容加上自己的理解记录下来,方便日后查看。
当你敲入一行代码并交给Shell执行时,命令行处理就开始发挥作用了。
管道行
shell从标准输入或脚本中读取的每行称为一个管道行,它包含一个或多个由0个或多个管道字符
|
分割的命令。shell会对其读取的每个管道行执行以下操作,也称作命令行处理:命令行处理流程
这个过程可分为12个阶段:
空格、制表符、换行符、;、(、)、<、>、|、&
。if
等控制结构起始字符串、function
、{
、(
。则该命令为复合命令,shell将读取下一条命令,并重复这一过程。else
、then
、fi
等或逻辑操作符,则shell给出语法错误。~
位于单词头,则使用用户的主目录替换~单词
$
开头的表达式执行变量替换。$(string)
进行命令替换$((string))
算术表达式$IFS
。*
、?
、[
、]
执行路径名扩展。函数 > 内置命令 > $PATH里的脚本
的优先级查找第一个命令。这个过程复杂难记,我们可以挑选出容易搞混的几条,把他们的处理优先级记下来:
别名替换、大括号扩展、
~
扩展、变量替换、命令替换、算术替换、路径扩展、命令查找处理流程的改变
上面个的12个步骤其实是可以被改变的,其中引号就可以忽略某些步骤。
引用
'
,忽略前10个步骤,也就是把整个字符串当做一个单词,直接进行命令查找并执行。"
,只保留6、7、8以及11、12。命令查找顺序
而
command
可以忽略别名替换和函数查找:builtin
bulitin
只会查找内置命令。enable
enable
能屏蔽内置命令:处理流程的恢复
内置命令
eval
的作用就是重新执行命令行处理,表面上看起来像在程序运行时执行动态生成的字符串。eval
和JavaScript中的eval
一样。一个使用eval的例子: