Синтаксический анализ массива с использованием IFS с не-белыми пробелами создает пустые элементы.
Даже использования tr -s
для сокращения нескольких разделителей до одного раздела недостаточно.
Пример может объяснить проблему более четко.
Есть ли способ достичь "нормальных" результатов с помощью настройки IFS (есть ли связанные настройки для изменения поведения IFS? .... т.е. действовать так же, как пробельные символы по умолчанию МФС.
var=" abc def ghi "
echo "============== IFS=<default>"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
#
sfi="$IFS" ; IFS=':'
set -f # Disable file name generation (globbing)
# (This data won't "glob", but unless globbing
# is actually needed, turn if off, because
# unusual/unexpected combinations of data can glob!
# and they can do it in the most obscure ways...
# With IFS, "you're not in Kansas any more! :)
var=":abc::def:::ghi::::"
echo "============== IFS=$IFS"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
echo "============== IFS=$IFS and tr"
arr=($(echo -n "$var"|tr -s "$IFS"))
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
set +f # enable globbing
IFS="$sfi" # re-instate original IFS val
echo "============== IFS=<default>"
Вот вывод
============== IFS=<default>
# arr[0] "abc"
# arr[1] "def"
# arr[2] "ghi"
============== IFS=:
# arr[0] ""
# arr[1] "abc"
# arr[2] ""
# arr[3] "def"
# arr[4] ""
# arr[5] ""
# arr[6] "ghi"
# arr[7] ""
# arr[8] ""
# arr[9] ""
============== IFS=: and tr
# arr[0] ""
# arr[1] "abc"
# arr[2] "def"
# arr[3] "ghi"
============== IFS=<default>
Ответы:
Для удаления нескольких (не пробельных) последовательных символов-разделителей можно использовать два (строки / массива) расширения параметров. Хитрость заключается в том, чтобы установить
IFS
переменную в пустую строку для расширения параметра массива.Это описано в
man bash
разделе « Разделение слов» :источник
IFS=' '
(то есть пробел) ведет себя так же. Я нахожу это менее запутанным, чем явный нулевой аргумент ("" или ") изIFS
.С
bash
manpage:Это означает, что пробелы IFS (пробел, табуляция и новая строка) не обрабатываются как другие разделители. Если вы хотите получить точно такое же поведение с альтернативным разделителем, вы можете выполнить замену разделителя с помощью
tr
илиsed
:Эта
%#%#%#%#%
вещь является магическим значением для замены возможных пробелов внутри полей, ожидается, что она будет «уникальной» (или очень нелепой). Если вы уверены, что в полях никогда не будет места, просто отбросьте эту часть).источник
tr
привести примеры, чтобы показать проблему ... Я хочу избежать системного вызова, поэтому я посмотрю на вариант bash,${var##:}
который я упомянул в своем комментарии к ответу Глена ... Я подожду некоторое время .. может быть, есть способ уговорить IFS, в противном случае первая часть вашего ответа будет после ....IFS
одинакова во всех оболочках в стиле Борна, она указана в POSIX .IFS
символов в качестве разделительной строки. На мой вопрос лучше всего ответилиjon_d
, но ответ @ nazad показывает изящный способ использованияIFS
без циклов и служебных приложений.Поскольку bash IFS не предоставляет внутренний способ обработки последовательных символов-разделителей как одного разделителя (для разделителей без пробелов), я собрал версию полностью bash (в отличие от использования внешнего вызова, например, tr, awk, sed). )
Он может обрабатывать мульти-символ IFS ..
Вот его результаты во время выполнения, наряду с аналогичными тестами для параметров
tr
иawk
, показанными на этой странице Q / A ... Тесты основаны на 10000 итерациях простого построения массива (без ввода-вывода) ...Вот вывод
Вот сценарий
источник
Вы также можете сделать это с gawk, но это не красиво:
выходы
источник
$var
на${var##:}
... Я действительно пытался настроить IFS сам ... Я хочу сделать это без внешнего вызова (у меня есть ощущение, что bash может сделать это более эффективно, чем любой внешний ... может, я буду продолжать в том же духе) ... ваш метод работает (+1) .... Насколько далеко так как изменение ввода идет, я бы предпочел попробовать его с помощью bash, а не awk или tr (это позволило бы избежать системного вызова), но я действительноbash 1.276s
...call (awk) 0m32.210s
,,,call (tr) 0m32.178s
... Сделайте это несколько раз, и вы можете подумать, что bash медленный! ... проще ли в этом случае? ... нет, если у вас уже есть фрагмент :) ... я опубликую его позже; должен идти сейчас.var="The \"X\" factor:::A single '\"' crashes:::\"One Two\""
Ответ прост: сверните все разделители в один (первый).
Это требует цикла (который выполняется меньше, чем
log(N)
раз):Осталось только правильно разделить строку на один разделитель и вывести ее:
Нет необходимости
set -f
ни менять IFS.Протестировано с пробелами, символами новой строки и глобусными символами. Все работают. Довольно медленный (как и следовало ожидать от цикла оболочки).
Но только для bash (bash 4.4+ из-за опции
-d
readarray).ш
Версия оболочки не может использовать массив, единственный доступный массив - это позиционные параметры.
Использование
tr -s
- это всего одна строка (IFS не изменяется в скрипте):И распечатать это:
Все еще медленно, но не намного.
Команда
command
недопустима в Борне.В zsh
command
вызывает только внешние команды и приводит к сбою eval, еслиcommand
используется.В ksh, даже при том
command
, что значение IFS изменяется в глобальной области видимости.И
command
делает разделение неудачным в оболочках, связанных с mksh (mksh, lksh, posh). Удаление командыcommand
заставляет код работать на большем количестве оболочек. Но: удалениеcommand
заставит IFS сохранять свое значение в большинстве оболочек (eval - это специальная встроенная функция), за исключением bash (без режима posix) и zsh в режиме по умолчанию (без эмуляции). Эту концепцию нельзя заставить работать по умолчанию в zsh, с или безcommand
.Многосимвольный IFS
Да, IFS может быть многосимвольным, но каждый символ будет генерировать один аргумент:
Будет выводить:
С bash вы можете опустить
command
слово, если не в эмуляции sh / POSIX. Команда завершится ошибкой в ksh93 (IFS сохраняет измененное значение). В zsh командаcommand
заставляет zsh пытаться найтиeval
внешнюю команду (которую она не находит) и завершается неудачно.То, что происходит, - то, что единственные символы IFS, которые автоматически свернуты в один разделитель, являются пробелом IFS.
Один пробел в IFS свернет все последовательные пробелы в один. Одна вкладка свернет все вкладки. Один пробел и одна вкладка сворачивают серии пробелов и / или табуляций в один разделитель. Повторите идею с новой строкой.
Чтобы свернуть несколько разделителей, требуется некоторое жонглирование.
Предполагая, что ASCII 3 (0x03) не используется во входных данных
var
:Большинство комментариев о ksh, zsh и bash (about
command
и IFS) все еще применимы здесь.Значение
$'\0'
будет менее вероятным при вводе текста, но переменные bash не могут содержать NUL (0x00
).В sh нет внутренних команд для выполнения одинаковых строковых операций, поэтому tr - единственное решение для sh-скриптов.
источник
command eval
IIRC от Жиля