Order-Ambiguous Shell I/O重定向的一致性如何?

Jas*_*ick 2 bash shell parsing cmd io-redirection

这个引人入胜的帖子:

这个命令如何合法?"> file1 <file2 cat"

在一个看似格式错误的cat"shell"(一个Linux shell,大概是BASH)的调用中突出了令人惊讶的行为.基本上,shell似乎能够从一系列字符串中的模糊位置获取可执行文件,然后使用I/O重定向到流/文件描述符.

根据我的理解,基本过程是:

  1. 查找重定向模式并将其读入或读出适当的流/文件描述符(例如:1>(stdout))(这发生在命令中启动可执行进程之前!(例如cat调用))
  2. 在字符串列表中查找可执行进程.
  3. 启动该可执行过程
  4. 暂停进程完成或继续(根据需要)在步骤1中检测到的各种输出.

这导致了一些令人惊讶的逻辑.例如,在执行后的新目录中echo "dog" > cat:

  • <cat cat >dog:从文件中写入"狗" catdog使用shell工具cat

  • <cat cat> cat cat:覆盖第一个命令,留下一个空白cat文件(不确定在第二个命令的中间发生了什么).

  • <cat cat> cat cat >dog 2>more:创建空文件,dog并用空文件more覆盖cat文件.

  • <cat >dog cat cat <dog >cat(创建空文件dog,覆盖catw /空文件)

  • <cat cat >dog 2>much 1>more:用cat空文件覆盖; 创建文件dog/ more每个包含字符串"dog",创建为空much

(上述列表行为已在BASH(v4.3.46)上进行了测试.)

现在,在某种程度上,可怜的外壳决定它已经足够了.例如,面对:

<cat dog> cat cat >dog >cat

它抱怨说:

bash:dog:找不到命令

但是有一个额外的惊喜 - 命令实际上已部分完成.与上面的大多数示例一样,它cat使用空白文件覆盖文件,并创建了一个空白文件dog.

为了更好地理解" 最流行的Linux shell "和CMD(标准Windows shell)中的复杂I/O重定向处理:

  1. BASH (Linux)的
  2. TCSH (Linux)的
  3. KSH (Linux)的
  4. ZSH (Linux)的
  5. CMD (视窗)

...是这种顺序模糊的I/O重定向解析...

  1. 所有人都支持?(我只有时间测试BASH(Linux)和cmd(Windows).)
  2. 它是否支持所有支持的可执行文件或仅支持核心shell实用程序
  3. 这些shell用于处理流/描述符的清理/排序的规则是什么,特别是在根据子串的选择(例如,stuff.dat>1test.dat<2test.datwhere 1test.dat2test.datare文件)解析重定向看起来不明确的命令时
  4. 它们的解析规则在多大程度上在shell之间保持一致?
  5. 是什么决定了这些shell中具有复杂I/O重定向模式的命令的失败?

ric*_*ici 8

对于POSIX shell--即尝试实现Posix标准的shell - 解析算法实际上相当简单,并且还在该标准中进行了记录.这包括bash,kshzsh从你的列表(以及其他,如dash),但不能在Windows cmd.tcsh类似但不是Posix.

重定向不是"命令模糊".它们被解析并从左到右执行.唯一可能奇怪的部分是它们可能与命令及其参数任意交错,但由于每个重定向都在重定向操作符之前,因此不会产生歧义.

对于简单命令,过程大致如下:

  1. 该命令分为单词.重定向运算符之前的单词是重定向; 这些将从命令中删除并保存以供以后处理.

    需要注意的是重定向操作符是自定界,所以没有任何区别之间a> b,a >ba>b.所有这些都是单词a,重定向运算符>和单词b,>b并将被视为重定向.所以语法<a> b可能会让人类读者感到困惑(因此应该避免使用),但它不会混淆shell,因为shell将它视为以更正常的方式编写的<a >b.

  2. 开头的主要词ID=是赋值(其中ID任何看起来像变量名的东西).这些也被删除以便以后处理.与重定向不同,这些是在第一个单词(如果有的话)之前唯一被识别的,这不是赋值.

  3. 剩余的单词(如果有的话)根据扩展规则进行扩展,这可能涉及拆分扩展单词.扩展后的第一个单词(如果有)是命令,其余的单词是命令参数.

  4. 重定向从左到右执行.输出重定向(>foo)创建或截断命名文件; append redirections(>>foo)只创建文件.

  5. 分配已扩展并应用.如果有命令,则分配将应用于命令将运行的子shell环境; 否则,它们将应用于当前的shell环境.

  6. 如果有命令,则执行命令参数字作为参数传递给它argc/argv.

例如,<cat cat> cat cat似乎让你感到困惑的那条线从左到右解析为:

  • <cat,输入重定向
  • cat,一个命令
  • >cat,输出重定向
  • cat一个论点

这导致重定向<cat>catcat使用参数调用命令之前执行cat.<cat如果cat在执行该行之前该文件在当前目录中不存在,则第一个redirect()将失败,因此>cat只有在文件存在时才会执行第二个redirect(); 它会立即截断(清空)文件.除非当前目录在PATH中,否则cat将从文件执行该命令,该文件/bin/cat是另一个文件.由于向cat命令提供了一个参数,因此它不会使用其标准输入,因此<cat除非导致整个命令失败,否则重定向将无效,除非该文件cat已存在.由于cat在执行命令之前文件将被截断cat cat,因此不会将任何内容写入标准输出,并且文件cat将保持为空.

关于你的上一个问题:

  • 除了有关错误处理的一些细节之外,这些规则同样适用于所有简单命令,无论是否内置.

  • 2>2foo不特殊,所以2foo是一个文件名.FD重复用>&重定向运算符表示; >&2foo被视为复制的尝试2foo,这是无效的,因为2foo它不是整数.Posix将此视为未指定的行为,因此实际的shell可能会做得很好.有关详细信息,请参见Posix shell规范的第2.7.5节(或至少是官方线).

  • 由于文件不存在或文件的权限不允许操作,重定向可能会失败.如上所述,重定向从左到右执行,这可能在"复杂"情况下起作用.