Я знаю, что могу решить эту проблему несколькими способами, но мне интересно, есть ли способ сделать это, используя только встроенные средства bash, и если нет, каков наиболее эффективный способ сделать это.
У меня есть файл с содержимым, как
AAA
B C DDD
FOO BAR
под этим я имею в виду только несколько строк, и каждая строка может иметь или не иметь пробелов. Я хочу выполнить команду как
cmd AAA "B C DDD" "FOO BAR"
Если я использую, cmd $(< file)
я получаю
cmd AAA B C DDD FOO BAR
и если я использую, cmd "$(< file)"
я получаю
cmd "AAA B C DDD FOO BAR"
Как мне обработать каждую строку ровно одним параметром?
bash
command-substitution
Old Pro
источник
источник
Ответы:
Портабельно:
Или используя подоболочку для
IFS
локального изменения параметров:Оболочка выполняет разбиение поля и генерацию имени файла в результате подстановки переменной или команды, которые не заключены в двойные кавычки. Таким образом, вам нужно отключить генерацию имени файла с помощью
set -f
, и настроить разделение полей,IFS
чтобы сделать только новые строки отдельными полями.С конструкциями bash или ksh мало что можно получить. Вы можете сделать
IFS
локальным для функции, но нетset -f
.В bash или ksh93 вы можете хранить поля в массиве, если вам нужно передать их нескольким командам. Вы должны контролировать расширение во время создания массива. Затем
"${a[@]}"
расширяется до элементов массива, по одному на слово.источник
Вы можете сделать это с помощью временного массива.
Настроить:
Заполните массив:
Используйте массив:
Не могу найти способ сделать это без этой временной переменной - если только
IFS
изменение не имеет значенияcmd
, в этом случае:должен сделать это.
источник
IFS
всегда сбивает меня с толку.IFS=$'\n' cmd $(<input)
не работаетIFS=$'\n'; cmd $(<input); unset IFS
работает. Зачем? Я думаю, я буду использовать(IFS=$'\n'; cmd $(<input))
IFS=$'\n' cmd $(<input)
не работает, потому что он устанавливается толькоIFS
в средеcmd
.$(<input)
раскрывается для формирования команды перед выполнением присваиванияIFS
.Похоже, канонический способ сделать это
bash
что-то вродеили, если ваша версия bash имеет
mapfile
:Единственное различие, которое я могу найти между mapfile и циклом while-read по сравнению с однострочным
является то, что первый будет преобразовывать пустую строку в пустой аргумент, в то время как однострочный будет игнорировать пустую строку. В этом случае я бы предпочел однострочное поведение, так что двойной бонус за его компактность.
Я бы использовал,
IFS=$'\n' cmd $(<file)
но это не работает, потому что$(<file)
интерпретируется для формирования командной строки, прежде чемIFS=$'\n'
вступит в силу.Хотя в моем случае это не сработало, теперь я узнал, что многие инструменты поддерживают завершающие строки,
null (\000)
вместоnewline (\n)
которых многое облегчает работу, например, с именами файлов, которые являются распространенными источниками этих ситуаций. :направляет список полностью определенных имен файлов в качестве аргументов md5 без каких-либо глобализаций, интерполяции или чего-либо еще. Это приводит к не встроенному решению
хотя это также игнорирует пустые строки, хотя и захватывает строки, которые имеют только пробелы.
источник
cmd $(<file)
значений без кавычек (используя способность bash для разделения слов) всегда рискованно. Если есть какая-либо строка,*
она будет расширена оболочкой до списка файлов.Вы можете использовать Баш встроенный ,
mapfile
чтобы прочитать файл в массивили, не проверенный,
xargs
может сделать этоисточник
xargs
тоже не помогаетxargs -d
илиxargs -L
-d
опции, и онаxargs -L 1
запускает команду по одному разу в строке, но все еще разделяет аргументы на пустом месте.mapfile
это очень удобно для меня, так как захватывает пустые строки как элементы массива, чегоIFS
метод не делает.IFS
обрабатывает смежные символы новой строки как один разделитель ... Спасибо за представление, поскольку я не знал о команде (хотя, основываясь на входных данных OP и ожидаемой командной строке, кажется, что он на самом деле хочет игнорировать пустые строки).Лучший способ найти. Просто работает.
источник
IFS
в пространство, но вопрос заключается в том, чтобы не разделить на пространство?файл
Самый простой цикл (переносимый) для разделения файла на новые строки:
Который напечатает:
Да, по умолчанию IFS = spacetabnewline.
Почему это работает
IFS
требуется.set -f
нужно.-r
(raw) состоит в том, чтобы избежать удаления большей части обратной косой черты.Это не будет работать, если необходимо разделение и / или сглаживание. В таких случаях требуется более сложная структура.
Если вам нужно (все еще портативный):
IFS= read -r line
IFS=':' read -r a b c
.Разделить файл на какой-нибудь другой символ (не переносимый, работает с ksh, bash, zsh):
расширение
Конечно, заголовок вашего вопроса о разделении выполнения команды на новые строки, избегая разделения на пробелы.
Единственный способ получить разбиение из оболочки - это оставить расширение без кавычек:
Это контролируется значением IFS, и к расширениям без кавычек также применяется глобализация. Чтобы сделать эту работу, вам необходимо:
Снимите опцию globbing shell
set +f
:set + f IFS = '' cmd $ (<файл)
Конечно, это меняет значение IFS и глобализации для остальной части сценария.
источник