Переменная оболочки с пробелами, заключенная в кавычки для опции единой командной строки - PullRequest
14 голосов
/ 12 ноября 2009

Сценарии 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 таким способом не является общим решением).

Я думаю, что все сводится к получению переменной оболочки, содержащей пробелы, которые нужно анализировать как одну переменную командной строки, а не разбивать на пробелы. Простейший пример оболочки, который я могу придумать, - это создать файл с пробелами и попытаться перечислить его с помощью ls с переменной оболочки в качестве аргумента ls:

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

Это работает, но недопустимо, поскольку в autoconf я не могу изменить код оболочки:

$ 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\\"`"

и т. Д.

Это невозможно сделать в сценариях оболочки? Есть ли магическое цитирование, которое расширит переменную оболочки с пробелами в один аргумент командной строки?

Ответы [ 7 ]

9 голосов
/ 12 ноября 2009

Вы должны попытаться установить переменную окружения $IFS.

от man bash (1):

IFS - внутренний разделитель полей, используемый для разделения слов после раскрытия и разбить строки на слова с помощью встроенного read команда. Значением по умолчанию является '' новая строка пробела ''.

Например

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

Не забудьте установить $IFS назад, иначе произойдут странные вещи.

4 голосов
/ 13 ноября 2009

если вы дадите команду

 gcc -I"x y z"

в оболочке, тогда, конечно, единственный параметр командной строки "-Ix y z" будет передан в gcc. В этом нет сомнений. В этом и заключается весь смысл двойных кавычек: вещи внутри двойных кавычек НЕ подлежат разделению полей и, например, также не подлежат $ IFS.

Но вы должны быть осторожны с количеством нужных вам цитат. Например, если вы скажете

 file="a b" # 1

и тогда вы говорите

 ls $file # 2

что происходит, так это то, что содержимое файловой переменной - это «a b», а не «« a b »», потому что двойные кавычки были «съедены» при разборе строки 1. Замененное значение затем разделяется полями, и вы получаете ls для двух файлов 'a' и 'b'. Правильный способ получить то, что вы хотите, это

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

Теперь проблема в вашем исходном случае состоит в том, что когда вы устанавливаете переменную в строку, которая содержит двойные кавычки, двойные кавычки позже интерпретируются не как символы кавычек, а как обычные буквы. Вот почему, когда вы делаете что-то вроде

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

фактически оболочка разбивает содержимое файловой переменной на «a» и «b» при анализе команды ls; двойная кавычка больше не является символом кавычки оболочки, а является лишь частью содержимого переменной. Это аналогично тому, если вы установите

 file="\$HOME"; ls $file

вы получаете сообщение об ошибке, что каталог '$ HOME' не существует --- поиск переменных среды не выполняется.

Так что ваши лучшие варианты

  1. Hack autoconf
  2. Не использовать имена путей с пробелами (лучшее решение)
2 голосов
/ 16 ноября 2009

Использование пробела в именах каталогов в мире Unix просто напрашивается на неприятности. Это не просто проблема цитирования в сценариях оболочки (что в любом случае должно быть сделано правильно): некоторые инструменты просто не могут справиться с пробелами в именах файлов. Например, вы не можете (переносимо) написать правило Makefile, которое говорит build "baz.o" из "foo bar/baz.c".

В случае CPPFLAGS выше, я бы в порядке предпочтения:

  1. исправлена ​​ошибка, из-за которой система не использовала пробелы в именах каталогов
  2. написать небольшую оболочку вокруг компилятора и вызвать ./configure CC=mygcc. В этом случае mygcc может быть
    
    <h1>!/bin/sh</h1>
    
    gcc "-I/foo bar/include" "$@"
    
  3. создайте символическую ссылку (например, /tmp/mypath) на страшный путь и используйте CPPFLAGS=-I/tmp/mypath
1 голос
/ 13 ноября 2009

Вы хотите процитировать весь аргумент любым из следующих способов:

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

Команда ./configure затем видит один аргумент

"CPPFLAGS=-I/path with space"

, который анализируется как параметр с именем «CPPFLAGS», имеющий значение «-I/path with space» (скобки добавлены для ясности) .

0 голосов
/ 07 апреля 2016

Все зависит от того, как используется переменная. Во-первых, обратите внимание, что если вы используете Autoconf, это, вероятно, означает, что в конечном итоге будет использоваться make, так что правила продиктованы make и, в частности, правилами make по умолчанию. Даже если вы захотите использовать исключительно свои собственные правила, между инструментами все должно быть согласованно, а некоторые переменные имеют стандартные значения, чтобы вы не хотели отклоняться от них. Это не относится к CPPFLAGS, но должно оставаться аналогичным CFLAGS, которое является стандартным. См. Утилиту POSIX make , в которой переменные просто раскрываются с помощью стандартного sh разбиения слов, которое не предоставляет никакого механизма цитирования (разделитель полей управляется $IFS, но не изменяет IFS переменная для приема пробелов в качестве обычных символов, поскольку это может нарушить другие вещи, например, возможность обеспечить несколько опций -I и / или -L в таких переменных стандартным способом).

Поскольку существует такое ограничение с make, я полагаю, что было бы бесполезно пытаться обойти это ограничение в Autoconf.

Теперь, поскольку пробел - это обязательно разделитель полей, единственная возможность - указывать имена путей без пробелов. Если пробелы в путевых именах будут поддерживаться в будущем, это, вероятно, будет сделано с помощью кодировки путевых имен с декодированием в пользовательском интерфейсе высокого уровня (немного похожем на URL-адреса). В качестве альтернативы, если у вас есть выбор и вы действительно хотите использовать пробелы в путевых именах, вы можете использовать пространство не ASCII (кстати, именно так RISC OS поддерживает пробел в путевых именах, заставляя его быть пробелом без прерываний).

0 голосов
/ 18 июля 2012
$ file="\"a b\""

$ eval ls $file
0 голосов
/ 13 ноября 2009

Использование цитат интересно. Из (легкого) прочтения справочной страницы bash я подумал, что вам нужно выйти из пробела с помощью \, поэтому "/ path with space" становится / path \ with \ space Я никогда не пробовал кавычки, но кажется, что это работать вообще (твой пример ls). Escape работает с ls без кавычек и без изменения IFS.

Что произойдет, если вы используете формат команды «Выходящие пробелы»?

...