AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 526574
Accepted
Sergiy Kolodyazhnyy
Sergiy Kolodyazhnyy
Asked: 2019-06-25 01:10:32 +0800 CST2019-06-25 01:10:32 +0800 CST 2019-06-25 01:10:32 +0800 CST

为什么大括号命令组在 POSIX Shell 语法中的左大括号后需要空格?

  • 772

TL;DR:为什么 POSIX 大括号组在{保留字后需要空格,而 subshel​​l 在保留字后不需要空格(?

POSIX shell 语法定义大括号组和子shell 如下

brace_group      : Lbrace compound_list Rbrace

subshell         : '(' compound_list ')'

现在,如果我们从字面上理解,空格很重要。这意味着必须有空格来描述左大括号和圆括号,如

{ echo hello world; }

( echo hello world )

这也将与复合命令定义保持一致:

这些复合命令中的每一个在开头都有一个保留字或控制运算符,在末尾有一个相应的终止符保留字或运算符。

然而,没有意义的是为什么(list)并且( list )工作得很好((不需要后面的空格),但是大括号扩展必须有一个前导空格,即{echo hello;}不起作用。

当然,保留字被视为 shell 字之后需要一个空格以与字段拆分的概念保持一致是有意义的,但是定义本身并没有提及空格。此外,如果{和(都被复合命令的 POSIX 定义视为保留字,为什么在这些保留字之后的空格字符方面对它们进行不同的处理?现在,ksh(1)手册确实说明了:

单词是字符序列,由不带引号的空白字符(空格、制表符和换行符)或元字符(<、>、|、;、&、(和))分隔

换句话说,ksh 将识别(为单词分隔符是有意义的,其中第一个单词将是命令或变量赋值。然而,POSIX 似乎没有提到(元字符。就 POSIX 语法而言,我发现的唯一可能的解释是它{被认为是一个“令牌”,其中 as(没有被列为一个。

/* These are reserved words, not operator tokens, and are
   recognized when reserved words are recognized. */


%token  Lbrace    Rbrace    Bang
/*      '{'       '}'       '!'   */

那么这种差异的确切原因是什么?

接受的答案说明:

  • 将接受的复选标记移至Isaac 的答案,因为它提供了标准本身的报价,直接解决了我的问题:

    例如,'(' 和 ')' 是控制运算符,因此<space>(list) 中不需要。但是,'{' 和 '}' 是 {list;} 中的保留字,因此在这种情况下,前导<space>和<semicolon>是必需的。

  • 接受 Kusalananda 的回答。Kusalananda 的回答解决了我的需要,尽管主要是从非正式和直观的角度来看;它指出{是保留字并且(是运算符。Michael Homer 在评论中也提到了同样的内容——复合命令定义声明(强调添加):

    这些复合命令中的每一个在开头都有一个保留字或控制运算符

  • {被定义为保留字,类似于foror while,列在 Shell Grammar 中(参见问题中的最后一个代码块)

  • 第 2.9 节指出(已添加重点):

    特别是,这些表示包括在不需要 s的某些地方的标记之间的间距<blank>(当其中一个标记是运算符时)。

  • 虽然标准没有明确定义(为运算符,但(称为运算符;具体来说,第 2.9.2 节说

    如果管道以保留字开头!并且 command1 是子 shell 命令,应用程序应确保 command1 开头的 ( 运算符与 ! 分隔一个或多个字符。保留字 ! 的行为紧跟 ( 运算符是未指定的。

  • Digital Trauma关于 Stack Overflow的问题指出了关于保留字的第 2.4 节:

    只有在没有引用任何字符并且该词被用作以下情况时,才会发生这种识别:

    - 命令的第一个字

  • 正如 Kusalananda 的回答“POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实上,大括号是保留字,这意味着它们必须被空格包围”正如迈克尔荷马在评论中提到的那样:“如果空格本身很重要,则需要在生产中列出它们”

结案。

shell posix
  • 2 2 个回答
  • 1027 Views

2 个回答

  • Voted
  1. Kusalananda
    2019-06-25T01:23:21+08:002019-06-25T01:23:21+08:00

    花括号和圆括号的区别在于花括号 (和!) 是保留字,就像for,等一样if,then而圆括号是控制运算符。单词需要用空格分隔。

    这意味着就像你不能拥有

    foriin*; do
    

    你不能拥有

    {somecommand;} >file
    

    或者

    if !somecommand; then
    

    POSIX 语法中显示的空格不是 shell 输入数据中需要存在的空格,而只是显示语法本身的一种方式。事实上,大括号是保留字,这意味着它们必须被空格包围,而子shell 的括号则不需要。

    • 13
  2. Best Answer
    user232326
    2019-06-25T14:35:39+08:002019-06-25T14:35:39+08:00

    这是 shell 将行分成标记的方式的限制。

    shell从输入文件中读取行,并根据第 2 节“Shell 介绍”将它们转换为单词或运算符:

    1. shell 将输入分解为标记:单词和运算符

    { 是保留字

    有些字是保留字

    保留字是对 shell 具有特殊意义的字。下列字应视为保留字:

    ! { } case do done elif else esac fi for if in then until while
    

    词,要被识别为词,必须是定界的。

    保留字仅在分隔时才被识别...

    主要是通过空格(第 7 点)和操作员。

    1. 如果当前字符是未引用的 <blank>,则包含前一个字符的任何标记都将被分隔,并且当前字符应被丢弃。

    ( 是一个运算符

    运营商自立门户:

    而运算符本身就是分隔符。

    其中“操作员”是:

    3.260 运算符

    在 shell 命令语言中,可以是控制运算符或重定向运算符。

    重定向运算符是:

    重定向运算符

    在 shell 命令语言中,执行重定向功能的标记。它是以下符号之一:

    <     >     >|     <<     >>     <&     >&     <<-     <>
    

    控制运算符是:

    3.113 控制操作员

    在 shell 命令语言中,执行控制功能的令牌。它是以下符号之一:

    &   &&   (   )   ;   ;;   newline   |   ||
    

    结论

    因此,'(' 和 ')' 是控制运算符,而 '{' '}' 是保留字。

    您的问题的完全相同的描述在规范中:

    例如,'(' 和 ')' 是控制运算符,因此 (list) 中不需要 <space>。但是,'{' 和 '}' 是 { list;} 中的保留字,因此在这种情况下需要前导 <space> 和 <semicolon>。

    这正好解释了为什么在{.

    这是有效的:

    { echo yes;}
    

    就像这样:

    {(echo yes);}
    

    这个:

    {(echo yes)}
    

    甚至这样:

    {>/dev/tty echo yes;}
    
    • 7

相关问题

  • 这个命令是如何工作的?mkfifo /tmp/f; 猫/tmp/f | /bin/sh -i 2>&1 | 数控 -l 1234 > /tmp/f

  • FreeBSD 的 sh:列出函数

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • grep -v grep 有什么作用

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve