Подстановка команд: разделение на новой строке, но не пробел

Я знаю, что могу решить эту проблему несколькими способами, но мне интересно, есть ли способ сделать это, используя только встроенные модули 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"

Как получить каждую строку, обработанную ровно одним параметром?

26 голосов | спросил Old Pro 28 Mayam12 2012, 01:22:41

4 ответа


8

Похож на канонический способ сделать это в bash что-то вроде

unset args
while IFS= read -r line; do 
    args+=("$line") 
done < file

cmd "${args[@]}"

или, если ваша версия bash имеет mapfile:

mapfile -t args < filename
cmd "${args[@]}"

Единственное различие, которое я могу найти между файлом map и циклом while-read в сравнении с однострочным

(set -f; IFS=$'\n'; cmd $(<file))

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

Я бы использовал IFS=$'\n' cmd $(<file), но он не работает, потому что $(<file) интерпретируется для формирования командной строки до того, как вступает в силу IFS=$'\n'.

Хотя в моем случае это не работает, теперь я узнал, что многие инструменты поддерживают завершающие строки с помощью null (\000) newline (\n), что делает это намного проще при работе с именами файлов, которые являются общими источниками этих ситуаций:

find / -name '*.config' -print0 | xargs -0 md5

подает список полностью квалифицированных имен файлов в качестве аргументов md5 без каких-либо подстановок или интерполяции или что-то еще. Это приводит к не встроенному решению

tr "\n" "\000" <file | xargs -0 cmd

, хотя это тоже игнорирует пустые строки, хотя и фиксирует строки, имеющие только пробелы.

ответил Old Pro 28 Mayam12 2012, 04:32:58
24

Портабельно:

set -f              # turn off globbing
IFS='
'                   # split at newlines only
cmd $(cat <file)
unset IFS
set +f

Или используя подоболочку, чтобы сделать код IFS и локальный вариант:

( set -f; IFS='
'; exec cmd $(cat <file) )

Оболочка выполняет разделение полей и генерация имени файла в результате замены переменной или подстановки команды, которая не находится в двойных кавычках. Поэтому вам нужно отключить создание имени файла с помощью set -f и настроить разделение полей с помощью IFS, чтобы сделать только новые строки для отдельных полей.

Существует не так много, чтобы получить конструкцию bash или ksh. Вы можете сделать IFS локальным для функции, но не set -f.

В bash или ksh93 вы можете хранить поля в массиве, если вам нужно передать их нескольким командам. Вы должны контролировать расширение во время сборки массива. Затем "${a[@]}" расширяется до элементов массива, по одному на каждое слово.

set -f; IFS=$'\n'
a=($(cat <file))
set +f; unset IFS
cmd "${a[@]}"
ответил Gilles 28 Mayam12 2012, 04:15:40
9

Вы можете сделать это с помощью временного массива.

Настройка:

$ cat input
AAA
A B C
DE F
$ cat t.sh
#! /bin/bash
echo "$1"
echo "$2"
echo "$3"

Заполните массив:

$ IFS=$'\n'; set -f; foo=($(<input))

Используйте массив:

$ for a in "${foo[@]}" ; do echo "--" "$a" "--" ; done
-- AAA --
-- A B C --
-- DE F --

$ ./t.sh "${foo[@]}"
AAA
A B C
DE F

Невозможно определить способ сделать это без этой временной переменной - если изменение IFS не важно для cmd, и в этом случае:

$ IFS=$'\n'; set -f; cmd $(<input) 

должен это сделать.

ответил Mat 28 Mayam12 2012, 01:35:25
3

Вы можете использовать встроенный bash mapfile, чтобы прочитать файл в массив

mapfile -t foo < filename
cmd "${foo[@]}"

или, untested, xargs может сделать это

xargs cmd < filename
ответил glenn jackman 28 Mayam12 2012, 02:03:30

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132