Если вам нужно сохранить выходные данные и вы хотите массив, mapfileэто легко.
Во-первых, подумайте, нужно ли вообще сохранять выходные данные вашей команды. Если нет, просто запустите команду.
Если вы решите, что хотите прочитать вывод команды в виде массива строк, это правда, что один из способов сделать это - отключить глобализацию, установить IFSразбиение на строки, использовать подстановку команд в ( )синтаксисе создания массива и выполнить сброс IFSпосле этого, что сложнее, чем кажется. Ответ Тердона охватывает некоторые из этого подхода. Но я предлагаю вам использовать встроенную команду mapfileоболочки Bash для чтения текста в виде массива строк. Вот простой случай, когда вы читаете из файла:
mapfile < filename
Это читает строки в массив с именем MAPFILE. Без перенаправления ввода вы будете читать из стандартного ввода оболочки (обычно вашего терминала) вместо файла с именем . Для чтения в массив, отличный от значения по умолчанию , передайте его имя. Например, это читает в массив :< filenamefilenameMAPFILElines
mapfile lines < filename
Другое поведение по умолчанию, которое вы можете изменить, состоит в том, что символы новой строки в концах строк остаются на месте; они появляются как последний символ в каждом элементе массива (если только ввод не завершился без символа новой строки, в этом случае последний элемент не имеет такового). Чтобы сжать эти новые строки, чтобы они не появлялись в массиве, передайте -tопцию mapfile. Например, это читает в массив recordsи не записывает завершающие символы новой строки в его элементы массива:
mapfile -t records < filename
Вы также можете использовать -tбез передачи имени массива; то есть он также работает с неявным именем массива MAPFILE.
mapfileОболочки встроенной опоры и другие варианты, и он в качестве альтернативы может быть вызван в качестве readarray. Запустите help mapfile(и help readarray) для деталей.
Но вы не хотите читать из файла, вы хотите читать из вывода команды. Чтобы добиться этого, используйте процесс подстановки . Эта команда читает строки из команды some-commandс arguments...аргументами командной строки и помещает их в mapfileмассив по умолчанию MAPFILEс удаленными завершающими символами новой строки:
mapfile -t < <(some-command arguments...)
Подстановка процесса заменяется реальным именем файла, из которого можно прочитать результаты выполнения. Файл является именованным каналом, а не обычным файлом, и в Ubuntu он будет именоваться как (иногда с другим номером, чем ), но вам не нужно заботиться о деталях, потому что оболочка заботится об этом все за кадром.<(some-command arguments...)some-command arguments.../dev/fd/6363
Вы можете подумать, что можете отказаться от замены процесса, используя вместо этого, но это не сработает, потому что, когда у вас есть конвейер из нескольких команд, разделенных Bash, запускает все команды в подоболочках . Таким образом оба и запускаются в своих собственных средах , инициализируемых, но отдельно от среды оболочки, в которой вы запускаете конвейер. В субоболочке , где работает, то массив имеет получить населенную, но затем этот массив отбрасываются , когда концы команд. никогда не создается и не изменяется для вызывающей стороны.some-command arguments... | mapfile -t|some-command arguments...mapfile -tmapfile -tMAPFILEMAPFILE
Вот как выглядит пример в ответе Тердона , если вы используете mapfile:
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
Вот и все. Вам не нужно проверять, IFSбыл ли он установлен , отслеживать, было ли оно установлено и с каким значением, устанавливать его на новую строку, а затем сбрасывать или сбрасывать его позже. Вам не нужно отключить подстановку (например, с set -f) , - которые действительно необходимы , если вы хотите использовать этот метод серьезно, так как имена файлов могут содержать *, ?и [--then повторно включить его ( set +f) впоследствии.
Вы также можете полностью заменить этот конкретный цикл одной printfкомандой - хотя это на самом деле не является преимуществом mapfile, так как вы можете сделать это независимо от того, используете ли вы mapfileили другой метод для заполнения массива. Вот более короткая версия:
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
Важно помнить, что, как упоминает Тердон , построчная работа не всегда уместна и не будет корректно работать с именами файлов, которые содержат переводы строк. Я рекомендую не называть файлы таким образом, но это может произойти, в том числе и случайно.
На самом деле не существует универсального решения.
Вы просили «общую команду для всех сценариев» и подход, основанный на использовании mapfileнескольких подходов к этой цели, но я призываю вас пересмотреть ваши требования.
Задача, показанная выше, лучше достигается с помощью одной findкоманды:
find . -type d -printf 'DIR: %p\n'
Вы также можете использовать внешнюю команду, например, sedдобавить DIR: в начало каждой строки. Это, возможно, несколько уродливо, и в отличие от этой команды find, она добавит дополнительные «префиксы» в имена файлов, содержащие символы новой строки, но она работает независимо от ее ввода, поэтому она как бы соответствует вашим требованиям и все же предпочтительнее считывать вывод в переменная или массив :
find . -type d | sed 's/^/DIR: /'
Если вам нужно перечислить, а также выполнить действие для каждого найденного каталога, например, запустить some-commandи передать путь к каталогу в качестве аргумента, то findвы тоже можете это сделать:
find . -type d -print -exec some-command {} \;
В качестве другого примера, давайте вернемся к общей задаче добавления префикса к каждой строке. Предположим, я хочу увидеть результат, help mapfileно нумеровать строки. Я бы на самом деле не использовал mapfileдля этого ни какой-либо другой метод, который считывает его в переменную оболочки или массив оболочки. Предположим help mapfile | cat -n, не дает нужного мне форматирования, я могу использовать awk:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
Чтение всех выходных данных команды в одну переменную или массив иногда полезно и целесообразно, но имеет серьезные недостатки. Вам не только приходится сталкиваться с дополнительной сложностью использования вашей оболочки, когда существующая команда или комбинация существующих команд может уже делать то, что вам нужно, либо лучше, но весь вывод команды должен храниться в памяти. Иногда вы можете знать, что это не проблема, но иногда вы можете обрабатывать большой файл.
Альтернатива, которую обычно пытаются (а иногда и делают правильно), - читать входные данные построчно read -rв цикле. Если вам не нужно сохранять предыдущие строки при работе с более поздними строками, и вам нужно использовать длинный ввод, тогда это может быть лучше, чем mapfile. Но этого также следует избегать в тех случаях, когда вы можете просто передать его команде, которая может выполнить эту работу, что и происходит в большинстве случаев .