【问题标题】:forcing brace expansion in backticks or why is this inconsistent?在反引号中强制大括号扩展或为什么这不一致?
【发布时间】:2017-10-15 08:54:51
【问题描述】:

当使用反引号(在 perl 中)时,我似乎与大括号扩展存在某种不一致

print `ls -d ~/{a,b}`;

工作正常,但是当我尝试时

print `ls -d ~/{a}`;

我明白了:

ls: cannot access /home/user/{a}: No such file or directory

我尝试了各种引用、间距和转义,但无济于事。我的问题是,有没有办法强制扩张?我意识到如果我自己把这些东西都搞定,我可以一起避免这个问题,但我对这种行为很好奇

我在 bash 和 tcsh 下都试过了。当直接在 tcsh 下使用时,该命令有效,但在 bash 下则无效

【问题讨论】:

  • bashtcsh 是两个完全不同的shell;你实际使用的是哪个?
  • print 甚至不是一个有效的bash 命令
  • @chepner 这是 perl...

标签: linux bash perl shell tcsh


【解决方案1】:

现在,经过您的多次编辑,我了解到您可能正在使用两种不同的 shell。而且,调用 shell 的是 perl,而不是你。

所以,要涵盖三个要素:

重击

在 bash 中。一个简单的 {a} 不会被扩展:

$ echo ~/{a,b}
/home/user/a /home/user/b

$ echo ~/{a}
/home/user/{a}

ls 将其作为文件搜索,但未找到。

手册 man bash 的相关部分是(强调我的):

格式正确的大括号扩展必须包含未加引号的左大括号和右大括号,以及至少一个未加引号的逗号或有效的序列表达式。

{a} 缺少必需的逗号。

唯一的选择是实际使用

$ print `ls -d ~/a`

tcsh

tcsh 中:一个简单的{a} 将被扩展(就像在csh shell 中一样):
使用> 作为 tcsh 提示符的 shell 指示符:

> echo --{a,b}++
 --a++ --b++

> echo --{a}++
--a++

作为一个简单的{} 也会:

$ echo --{}++
 --++

不会删除的大括号只有 {}{}
IIF(当且仅当)它们被识别为“单词”。最常被空格包围:

$ echo --{}++ == {} .. { :: } aa { bb
--++ == {} .. { :: } aa { bb

如果{} 出现在单词的部分 中,没有匹配的大括号,则错误:

> echo aa{bb
Missing '}'.

man tcsh 上对此的唯一描述真的很短(不清楚?):

> As a special case the words `{', `}' and `{}' are passed undisturbed.

perl

如果shell命令是called from perl,在调用shell的情况下,比如在Unix系统下,调用的shell是/bin/sh

另一个转折点是,/bin/sh 在大多数 Linux 系统中由 bash、tcsh in FreeBSDksh or csh in OpenBSD 提供服务。

很难预测名为/bin/sh 的 perl 的输出。

您可以确保调用了特定的 shell:

#!/usr/bin/perl
print `/bin/tcsh -c 'ls -d ~/{a,b}'`;

现在问题仍然存在:

  • 为什么需要对单个字符使用大括号扩展?

【讨论】:

  • 不幸的是,我要到下周才能访问系统,但我知道问题是在 shell 中执行 'ls -d ~/{a}' 确实有效。我现在将其标记为已解决,并在下周更新。谢谢!
  • Re "在 shell 中执行 'ls -d ~/{a}' 确实有效",你真的使用过/bin/sh吗?
  • @arrow 经过进一步检查,似乎在 tcsh 下 ls 有效,但在 bash 下却没有。但是,为什么在 tcsh 下使用 perl 时不起作用?
  • Perl 不在乎你使用的是什么外壳。反引号使用 /bin/sh。
  • @Nullman 扩展了 tcsh 和 perl 的部分。现在所有细节都应该清楚了。还有其他问题吗?。
【解决方案2】:

当您在使用 shell 命令时遇到问题时,请在 Perl(或任何程序)之外尝试它们以查看它们的作用:

重击:

$ ls ~/{a}
ls: /Users/brian/{a}: No such file or directory
$ ls ~/{a,b}
ls: /Users/brian/a: No such file or directory
ls: /Users/brian/b: No such file or directory

Tsch:

% ls ~/{a}
ls: /Users/brian/a: No such file or directory
% ls ~/{a,b}
ls: /Users/brian/a: No such file or directory
ls: /Users/brian/b: No such file or directory

因此,您会看到两个 shell 以不同的方式扩展 glob。来自bash docs

格式正确的大括号展开必须包含不带引号的左大括号和右大括号,以及至少一个不带引号的逗号或有效的序列表达式。任何格式不正确的大括号扩展都保持不变。

来自tcsh docs 有点欠缺,因为它没有提到您不需要逗号进行扩展:

元注释“a{b,c,d}e”是“abe ace ade”的简写。保留从左到右的顺序:“/usr/source/s1/{oldls,ls}.c”扩展为“/usr/source/s1/oldls.c /usr/source/s1/ls.c”。匹配结果在低级别单独排序以保留此顺序:“../{memo,*box}”可能会扩展为“../memo ../box ../mbox”。 (请注意,'memo' 没有与匹配 '*box' 的结果一起排序。)当此构造扩展到不存在的文件时,这不是错误,但可能会从扩展列表通过。该构造可以嵌套。作为一种特殊情况,单词 '{'、'}' 和 '{}' 会不受干扰地传递。

现在,在 Perl 中,反引号使用 /bin/sh。当您启动 perl 进程时,它并不关心您在哪个 shell。来自perlop

一个(可能)被插值的字符串,然后作为系统命令使用 /bin/sh 或其等效命令执行。

这就是您看到 bash 行为的原因。但是,您可以使用您喜欢的任何 shell(并且在系统上可用):

my $output = `tcsh -c 'ls -l ~/{a}'`

【讨论】:

猜你喜欢
  • 2013-06-13
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-10
  • 1970-01-01
  • 2015-08-11
相关资源
最近更新 更多