Переменная оболочки с пробелами, цитирование для одной опции командной строки

Сценарии 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\\"`"

и так далее.

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


person Patrick    schedule 12.11.2009    source источник


Ответы (7)


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

от человека Баш (1):

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

Например

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

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

person Chen Levy    schedule 12.11.2009
comment
Я бы установил IFS в нулевую строку; это работает так же хорошо. saveIFS="$IFS"; IFS=''; ls $file; IFS="$saveIFS" - person Dennis Williamson; 12.11.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. Взлом автоконф
  2. Не используйте пути с пробелами (лучшее решение)
person Antti Huima    schedule 13.11.2009

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

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

  1. исправить систему, не использующую пробелы в именах каталогов
  2. write a small wrapper around the compiler and call ./configure CC=mygcc. In that case mygcc might be
    
    

    !/бин/ш

    gcc "-I/foo bar/include" "$@"
  3. создайте символическую ссылку (например, /tmp/mypath) на страшный путь и используйте CPPFLAGS=-I/tmp/mypath
person adl    schedule 16.11.2009

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

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

Затем команда ./configure видит один аргумент

"CPPFLAGS=-I/path with space"

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

person David R Tribble    schedule 13.11.2009

Использование кавычек интересно. Из (слегка) прочитав справочную страницу bash, я подумал, что вам нужно избегать пробела с помощью \, поэтому «/ путь с пробелом» становится / путь \ с \ пробелом. Я никогда не пробовал кавычки, но кажется, что это не так. работают в целом (ваш пример ls). Экранирование работает с ls без кавычек и без изменения IFS.

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

person james    schedule 12.11.2009
comment
Суть в том, чтобы использовать ls в качестве теста и не трогать его аргумент. Требование состоит в том, чтобы иметь возможность делать ls $file точно так же и вносить изменения только в file="a b". Это аналог изменения аргумента на configure без изменения задействованных файлов. - person Dennis Williamson; 13.11.2009

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

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

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

person vinc17    schedule 07.04.2016

person    schedule
comment
Это не решит проблему, потому что для вызова eval потребуется изменить сценарий autoconf. Суть вопроса в том, чтобы избежать этой модификации. - person Steve K; 11.10.2012