В некоторых оболочках (в том числе bash
):
IFS=: command eval 'p=($PATH)'
(с помощью bash
, вы можете опустить, command
если не в эмуляции sh / POSIX). Но имейте в виду, что при использовании переменных без кавычек вам, как правило, это необходимо set -f
, и в большинстве оболочек для этого нет локальной области действия.
С Zsh вы можете сделать:
(){ local IFS=:; p=($=PATH); }
$=PATH
заключается в принудительном разделении слов, которое по умолчанию не выполняется zsh
(глобализация при раскрытии переменных также не выполняется, поэтому вам не нужно ничего, set -f
кроме эмуляции).
(){...}
(или function {...}
) называются анонимными функциями и обычно используются для установки локальной области видимости. с другими оболочками, которые поддерживают локальную область видимости в функциях, вы можете сделать что-то похожее с:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Чтобы реализовать локальную область видимости для переменных и параметров в оболочках POSIX, вы также можете использовать функции, представленные по адресу https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh . Тогда вы можете использовать его как:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(кстати, разделение $PATH
таким способом выше недопустимо , за исключением случаев, zsh
когда, как и в других оболочках, IFS является разделителем полей, а не разделителем полей).
IFS=$'\n' a=($str)
Это просто два задания, одно за другим просто так a=1 b=2
.
Примечание объяснения на var=value cmd
:
В:
var=value cmd arg
Оболочка выполняется /path/to/cmd
в новом процессе и проходит cmd
и arg
в argv[]
и var=value
в envp[]
. На самом деле это не присвоение переменных, а передача переменных окружения в исполняемую команду. В оболочке Bourne или Korn set -k
вы можете даже написать это cmd var=value arg
.
Теперь это не относится к встроенным функциям или функциям, которые не выполняются . В Bourne оболочки, в var=value some-builtin
, в var
конце концов быть установлен после этого, так же , как с в var=value
одиночку. Это означает, например, что поведение var=value echo foo
(которое бесполезно) варьируется в зависимости от того echo
, встроен он или нет.
POSIX и / или ksh
изменил это так, что поведение Борна происходит только для категории встроенных функций, называемых специальными встроенными функциями . eval
это особый встроенный, read
нет. Для не специальных встроенных функций, var=value builtin
устанавливает var
только для выполнения встроенной функции, которая заставляет его вести себя так же, как при выполнении внешней команды.
command
Команда может быть использована для удаления специального атрибута этих специальных встроенных команд . Однако POSIX упустил из виду то, что для встроенных eval
и .
встроенных команд это означало бы, что оболочкам придется реализовывать стек переменных (даже если он не указывает команды ограничения local
или typeset
области действия), потому что вы можете сделать:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Или даже:
a=1 command eval myfunction
с myfunction
является функцией использования или установки $a
и потенциально вызова command eval
.
Это было действительно упущением, потому что ksh
(на котором спецификация в основном основана) не реализовало его (а AT & T ksh
и zsh
до сих пор не реализовало ), но в настоящее время, за исключением этих двух, большинство оболочек реализуют его. Поведение варьируется среди оболочек, хотя в таких вещах, как:
a=0; a=1 command eval a=2; echo "$a"
хотя. Использование local
поддерживаемых оболочек - более надежный способ реализации локальной области видимости.
IFS=: command eval …
устанавливаетIFS
только для продолжительностиeval
, как предписано POSIX, в dash, pdksh и bash, но не в ksh 93u. Необычно видеть, что ksh нечетный, не соответствует требованиям.Стандартные сохранения и восстановления, взятые из «Среды программирования Unix» Керниганом и Пайком:
источник
$IFS
правильно, если он был ранее не установлен.$'\t\n'' '
, как описано здесь: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. пространство должно быть первым, как это используется для"$*"
. Обратите внимание, что это одинаково во всех оболочках типа Борна.Поместите ваш скрипт в функцию и вызовите эту функцию, передав ей аргументы командной строки. Поскольку IFS определяется локально, его изменения не влияют на глобальный IFS.
источник
Для этой команды:
Существует альтернативное решение: дать первому назначению (
IFS=$'\n'
) команду для выполнения (функцию):Это поместит IFS в среду для вызова split, но не будет сохранено в текущей среде.
Это также позволяет избежать рискованного использования eval.
источник
$IFS
установленным$'\n'
впоследствии, как того требует POSIX.Предложенный ответ от @helpermethod, безусловно, представляет собой интересный подход. Но это также немного ловушка, потому что в BASH область видимости локальной переменной простирается от вызывающей до вызываемой функции. Следовательно, установка IFS в main () приведет к тому, что это значение сохранится в функциях, вызываемых из main (). Вот пример:
И вывод ...
Если IFS, объявленный в main (), по-прежнему не находится в области видимости в func (), то массив не был бы должным образом проанализирован в func (). B. Раскомментируйте первую строку в func (), и вы получите следующий вывод:
Что вы должны получить, если IFS вышел за рамки.
ИМХО, гораздо лучшее решение - это отказаться от изменения или полагаться на IFS на глобальном / местном уровне. Вместо этого создайте новую оболочку и возьмите с собой IFS. Например, если вы вызвали func () в main () следующим образом, передав массив в виде строки с разделителем обратной косой черты:
... это изменение IFS не будет отражено в функции func (). Массив будет передан в виде строки:
... но внутри func () IFS все равно будет "/" (как установлено в main ()), если не будет изменено локально в func ().
Дополнительную информацию об изоляции изменений в IFS можно посмотреть по следующим ссылкам:
Как преобразовать переменную массива bash в строку, разделенную символами новой строки?
Bash строка для массива с IFS
Советы и рекомендации по общему программированию сценариев оболочки - см. «ПРИМЕЧАНИЕ об использовании вложенных оболочек ...»
источник
IFS=$'\n' declare -a astr=(...)
отлично, спасибо!Этот фрагмент из вопроса:
интерпретируется как два отдельных назначения глобальных переменных, оцениваемых слева направо, и эквивалентно:
или
Это объясняет, почему глобальный объект
IFS
был изменен, и почему разбиение слов$str
на элементы массива было выполнено с использованием нового значенияIFS
.Вы можете испытать желание использовать подоболочку, чтобы ограничить эффект
IFS
модификации следующим образом:но вы быстро заметите, что модификация
a
также ограничена подоболочкой:Затем у вас возникнет соблазн сохранить / восстановить IFS, используя решение из предыдущего ответа @msw, или попытаться использовать
local IFS
внутреннюю функцию, предложенную @helpermethod. Но довольно скоро вы заметите, что у вас много неприятностей, особенно если вы являетесь автором библиотеки, который должен быть устойчивым к неправильным действиям вызывающих скриптов:IFS
изначально был не установлен?set -u
(акаset -o nounset
)?IFS
было сделано только для чтения черезdeclare -r IFS
?trap
обработчиком)?Пожалуйста, не сохраняйте / восстанавливайте IFS. Вместо этого придерживайтесь временных модификаций:
Чтобы ограничить изменение переменной одной командой, встроенным или вызовом функции, используйте
IFS="value" command
.Чтобы прочитать несколько переменных, разделив их по определенному символу (
:
используется в качестве примера ниже), используйте:Для чтения в массив используйте (вместо этого
array_var=( $str )
):Ограничьте эффекты изменения переменной до подоболочки.
Чтобы вывести элементы массива через запятую:
Чтобы записать это в строку:
источник
Самое простое решение - взять копию оригинала
$IFS
, как, например, в ответе msw. Однако в этом решении не проводится различие между незаданнымIFS
иIFS
множеством, равным пустой строке, что важно для многих приложений. Вот более общее решение, которое отражает это различие:источник