15

Autoconf 脚本对带有空格的文件名或路径名有问题。例如,

./configure CPPFLAGS="-I\"/path with space\""

结果(config.log):

configure:3012: gcc  -I"/path with space"  conftest.c  >&5
gcc: with: No such file or directory
gcc: space": No such file or directory

来自 ./configure 的编译命令是ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5',我无法修改它(我也许可以,但以这种方式解决 autoconf 并不是一个通用的解决方案)。

我认为归结为获得一个包含空格的shell变量,以将其解析为单个命令行变量,而不是在空格处拆分。我能想到的最简单的 shell 示例是创建一个带有空格的文件并尝试列出ls一个 shell 变量作为参数ls

$ touch "a b"
$ file="a b"
$ ls $file
ls: a: No such file or directory
ls: b: No such file or directory

这可行,但是是非法的,因为在 autoconf 中我无法修改 shell 代码:

$ ls "$file"
a b

以下引用事物的尝试均无效:

$ file="\"a \"b"; ls $file
ls: "a: No such file or directory
ls: b": No such file or directory
$ file="a\ b"
$ file="a\\ b"
$ file="`echo \\"a b\\"`"

等等。

这是不可能在 shell 脚本中完成的吗?是否有一个神奇的引用可以将带有空格的 shell 变量扩展为单个命令行参数?

4

7 回答 7

9

您应该尝试设置$IFS环境变量。

来自 man bash(1):

IFS - 内部字段分隔符,用于扩展后的分词并使用 read 内置命令将行拆分为单词。默认值为“空格制表符换行符”。

例如

IFS=<C-v C-m>  # newline
file="a b"
touch $file
ls $file

不要忘记$IFS退后一步,否则会发生奇怪的事情。

于 2009-11-12T17:44:15.307 回答
4

如果你发出命令

 gcc -I"x y z"

在 shell 中,那么单个命令行参数“-Ix y z”肯定会被传递给 gcc。毫无疑问。这就是双引号的全部含义:例如,双引号内的内容不受字段拆分的影响,因此也不受 $IFS 的影响。

但是你需要小心你需要的报价数量。例如,如果你说

 file="a b" # 1

然后你说

 ls $file # 2

发生的情况是文件变量的内容是“a b”,而不是“a b”,因为在解析第 1 行时双引号被“吃掉”了。替换后的值是字段分隔的,你会在两个文件 'a' 和 'b' 上得到 ls。得到你想要的东西的正确方法是

 file="a b"; ls "$file"

现在,您原始情况的问题是,当您将变量设置为包含双引号的字符串时,双引号稍后不会被解释为 shell 引号符号,而只是作为普通字母。这就是为什么当你做类似的事情时

 file="\"a b\""; ls $file

实际上,当分析 ls 命令时,shell 将文件变量的内容标记为 '"a' 和 'b"';双引号不再是 shell 引号字符,而只是变量内容的一部分。这类似于如果你设置

 file="\$HOME"; ls $file

您会收到“$HOME”目录不存在的错误---没有进行环境变量查找。

所以你最好的选择是

  1. 破解自动配置
  2. 不要使用带空格的路径名(最佳解决方案)
于 2009-11-13T17:03:44.353 回答
2

在 Unix 世界中,在目录名中使用空格只是自找麻烦。这不仅仅是在 shell 脚本中引用的问题(无论如何都需要正确完成):一些工具根本无法处理文件名中的空格。例如,您不能(可移植地)编写一条Makefile规则说baz.o从“ ”构建“ foo bar/baz.c”。

在上述情况下CPPFLAGS,我会按优先顺序:

  1. 修复系统不使用目录名称中的任何空格
  2. 在编译器周围编写一个小包装器并调用./configure CC=mygcc. 在那种情况下mygcc可能是

    !/bin/sh

    gcc "-I/foo bar/include" "$@"
  3. 创建一个指向/tmp/mypath可怕路径的符号链接(例如)并使用CPPFLAGS=-I/tmp/mypath
于 2009-11-16T11:23:44.820 回答
1

您想通过以下任何一种方式引用整个论点:

./configure "CPPFLAGS=-I/path with space"
./configure CPPFLAGS="-I/path with space"

然后该./configure命令会看到一个参数

"CPPFLAGS=-I/path with space"

它被解析为一个名为«CPPFLAGS»具有值的参数«-I/path with space» (为清楚起见添加了括号)

于 2009-11-13T19:09:30.073 回答
0

使用引号很有趣。从(轻轻地)阅读 bash 手册页,我认为您必须用 \ 转义空格,因此“/path with space”变为 /path\ with\ space 我从未尝试过引号,但似乎没有一般工作(你的 ls 例子)。转义可与 ls 一起使用,无需引用且无需更改 IFS。

如果您使用命令的“转义空格”格式会发生什么?

于 2009-11-12T22:38:37.557 回答
0
$ file="\"a b\""

$ eval ls $file
于 2012-07-18T10:32:25.587 回答
0

一切都取决于变量的使用方式。首先,请注意,如果您使用的是 Autoconf,这可能意味着make最终将使用它,因此规则由 指定make,特别是默认make规则。即使您可能想专门使用自己的规则,但工具之间必须保持一致,并且某些变量具有标准含义,因此您不想偏离它们。这不是 的 情况CPPFLAGS,但这应该保持类似于CFLAGS,这是标准的。请参阅POSIXmake实用程序,其中变量仅使用标准sh分词进行扩展,它不提供任何引用机制(字段分隔符由 控制$IFS,但不要更改IFS变量接受空格作为普通字符,因为这会破坏其他事情,例如能够以标准方式在此类变量中提供多个-I和/或选项)。-L

由于 存在这样的限制make,我认为在 Autoconf 中试图避免这种限制是没有用的。

现在,由于空格必须是字段分隔符,唯一的可能是提供不带空格字符的路径名。如果将来要支持路径名中的空格,这可能会通过路径名编码来完成,并在高级 UI 中进行解码(有点像 URL 的)。或者,如果您可以选择并且真的想在路径名中使用空格,您可以使用一些非 ASCII 空格(顺便说一句,这就是 RISC OS 支持路径名中的空格的方式,通过强制它成为不间断空格)。

于 2016-04-07T12:13:33.423 回答