Shell变量带空格,引用单个命令行选项

时间:2009-11-12 17:40:03

标签: bash shell autoconf

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变量扩展为单个命令行参数?

7 个答案:

答案 0 :(得分:9)

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

来自man bash(1):

  

IFS - 用于分词的内部字段分隔符         在扩展之后,用read builtin将行分成单词         命令。默认值为''space tab newline''。

例如

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

不要忘记设置$IFS或发生奇怪的事情。

答案 1 :(得分: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"

现在原始案例中的问题是,当您将变量设置为CONTAINS双引号的字符串时,双引号稍后不会被解释为shell引号符号,而是正常字母。这就是你做

之类的事情的原因
 file="\"a b\""; ls $file

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

,它就类似于
 file="\$HOME"; ls $file

您收到'$ HOME'目录不存在的错误 - 没有发生环境变量查找。

所以你最好的选择是

  1. Hack autoconf
  2. 不要使用带空格的路径名(最佳解决方案)

答案 2 :(得分: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

答案 3 :(得分:1)

您想以下列两种方式引用整个论点:

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

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

"CPPFLAGS=-I/path with space"

被解析为名为«CPPFLAGS»的参数,其值为«-I/path with space» (为清晰起见,添加了括号)

答案 4 :(得分:0)

使用引号很有趣。从(轻轻地)阅读bash手册页我认为你必须用\来逃离空间,因此“/ space with space”变成/ path \ with \ space我从未尝试过引号,但似乎它没有一般的工作(你的例子)。转义使用ls而不引用且不更改IFS。

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

答案 5 :(得分:0)

$ file="\"a b\""

$ eval ls $file

答案 6 :(得分:0)

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

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

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