Как разделить вывод команды к отдельным строкам

list=`ls -a R*`
echo $list

В сценарии оболочки эта команда эха перечислит все файлы из текущего каталога, запускающегося с R, но в одной строке. Как я могу распечатать каждый объект на одной строке?

Мне нужна групповая команда для всех сценариев, происходящих с ls, du, find -type -d, и т.д.

8
задан 2 November 2017 в 04:45

4 ответа

Если вывод команды содержит несколько строк, то заключите свои переменные в кавычки для сохранения тех новых строк при повторении:

echo "$list"

Иначе оболочка развернет и разделит переменное содержание на пробеле (пробелы, новые строки, вкладки), и они будут потеряны.

9
ответ дан 23 November 2019 в 05:21

Вместо опрометчивого помещения ls вывод в переменной и затем echoлуг это, который удаляет все цвета, использует

ls -a1

От man ls

       -1     list one file per line.  Avoid '\n' with -q or -b

Я не советую сделать что-либо с выводом ls, кроме дисплея это :)

Используйте, например, шарики оболочки и a for цикл, чтобы сделать что-то с файлами...

shopt -s dotglob                  # to include hidden files*

for i in R/*; do echo "$i"; done

* это не будет включать текущий каталог . или его родитель .. хотя

14
ответ дан 23 November 2019 в 05:21

В то время как помещение его в кавычках как @muru предложенный действительно сделает то, что Вы попросили, Вы могли бы также хотеть рассмотреть использование массива для этого. Например:

IFS=$'\n' dirs=( $(find . -type d) )

IFS=$'\n' говорит, что удар, чтобы только разделить вывод на символах новой строки o получает каждый элемент массива. Без него он разделит на пробелах, таким образом, a file name with spaces.txt были бы 5 отдельных элементов вместо одного. Этот подход повредится, если Ваш файл/имена каталогов может содержать новые строки (\n)все же. Это сохранит каждую строку вывода команды как элемент массива.

Обратите внимание, что я также изменил старого стиля `command` кому: $(command) который является предпочтительным синтаксисом.

Вам теперь назвали массив $dirs, каждый bof, элементы которого являются строкой вывода предыдущей команды. Например:

$ find . -type d
.
./olad
./ho
./ha
./ads
./bar
./ga
./da
$ IFS=$'\n' dirs=( $(find . -type d) )
$ for d in "${dirs[@]}"; do
    echo "DIR: $d"
  done
DIR: .
DIR: ./olad
DIR: ./ho
DIR: ./ha
DIR: ./ads
DIR: ./bar
DIR: ./ga
DIR: ./da

Теперь, из-за некоторой странности удара (см. здесь), необходимо будет сбросить IFS назад к исходному значению после выполнения этого. Так, любое сохранение это и reasign:

oldIFS="$IFS"
IFS=$'\n' dirs=( $(find . -type d) ) 
IFS="$oldIFS"

Или сделайте это вручную:

IFS=" "$'\t\n '

С другой стороны, просто закройте текущий терминал. Вашему новому установят исходную IFS снова.

9
ответ дан 23 November 2019 в 05:21

Если необходимо сохранить вывод, и Вы хотите массив, mapfile помогает.

Во-первых, рассмотрите, необходимо ли сохранить вывод команды вообще. Если Вы не делаете, просто выполняете команду.

Если Вы решаете, что хотите считать вывод команды как массив строк, это верно, что один способ сделать это состоит в том, чтобы отключить globbing, установить IFS для разделения на строках используйте замену команды в ( ) синтаксис создания массива и сброс IFS впоследствии, который более сложен, чем это кажется. ответ terdon касается части того подхода. Но я предлагаю, чтобы Вы использовали mapfile, встроенная команда оболочки Bash для чтения текста как массив строк, вместо этого. Вот простой случай, где Вы читаете из файла:

mapfile < filename

Это читает строки в названный массив MAPFILE. Без перенаправления ввода < filename, Вы читали бы из стандартного входа оболочки (обычно Ваш терминал) вместо файла, названного filename. Читать в массив кроме значения по умолчанию MAPFILE, передайте его имя. Например, это читает в массив lines:

mapfile lines < filename

Другое поведение по умолчанию, которое Вы могли бы принять решение изменить, состоит в том, что символы новой строки в концах строк оставляют на месте; они появляются как последний знак в каждом элементе массива (если вход, законченный без символа новой строки, в этом случае последний элемент, не имеет одного). Для чавкания этих новых строк, таким образом, они не появляются в массиве передайте -t опция к mapfile. Например, это читает в массив records и не пишет запаздывание символов новой строки в его элементы массива:

mapfile -t records < filename

Можно также использовать -t не передавая имя массива; то есть, это работает с неявным названием массива MAPFILE, также.

mapfile окружите встроенные поддержки другие опции, и это может альтернативно быть вызвано как readarray. Выполненный help mapfilehelp readarray) для деталей.

Но Вы не хотите читать из файла, Вы хотите читать из вывода команды. Для достижения этого используйте замену процесса. Эта команда читает строки из команды some-command с arguments... как его параметры командной строки и места в них в mapfileмассив по умолчанию MAPFILE, с их удаленными символами новой строки завершения:

mapfile -t < <(some-command arguments...)

Замены замены процесса <(some-command arguments...) с фактическим именем файла, от который вывод выполнения some-command arguments... может быть считан. Файл является именованным каналом, а не регулярным файлом и на Ubuntu, как это назовут /dev/fd/63 (иногда с некоторым другим числом, чем 63), но Вы не должны интересоваться деталями, потому что оболочка заботится обо всем этом негласно.

Вы могли бы думать, что могли воздержаться от замены процесса при помощи some-command arguments... | mapfile -t вместо этого, но это не будет работать, потому что, когда у Вас будет конвейер нескольких команд, разделенных |, Выполнения Bash выполняют все команды в подоболочках. Таким образом оба some-command arguments... и mapfile -t выполненный в их собственных средах, инициализированных от, но отдельный от среды оболочки, в которой Вы выполняете конвейер. В подоболочке, где mapfile -t выполнения, MAPFILE массив действительно становится заполненным, но затем что массив отбрасывается, когда команда заканчивается. MAPFILE никогда не создается или изменяется для вызывающей стороны.

Вот то, на что пример в ответе terdon похож, полностью, если Вы используете mapfile:

mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
    echo "DIR: $d"
done

Именно. Вы не должны проверять если IFS был установлен, отслеживайте то, было ли не это установлено и с тем, какое значение, установило его на новую строку, затем сброс или повторно сбросило его позже. Вы не должны отключать globbing (например, с set -f) - который действительно необходим, если необходимо использовать тот метод серьезно, начиная с, имена файлов могут содержать *, ?, и [- затем повторно включите его (set +f) впоследствии.

Можно также заменить тот конкретный цикл полностью синглом printf команда - хотя это не на самом деле преимущество mapfile, поскольку можно сделать это, используете ли Вы mapfile или другой метод для заполнения массива. Вот более короткая версия:

mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"

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

Нет действительно единого решения.

Вы попросили "групповую команду для всех сценариев" и подход использования mapfile несколько подходы, что цель, но я убеждаю Вас пересмотреть свои требования.

Задача, показанная выше, лучше достигается только с синглом find команда:

find . -type d -printf 'DIR: %p\n'

Вы могли также использовать внешнюю команду как sed добавить DIR: к началу каждой строки. Это возможно несколько ужасно, и в отличие от той находки управляют, чтобы она добавила дополнительные "префиксы" в именах файлов, которые содержат новые строки, но она действительно работает независимо от своего входа так это, вид отвечает Вашим требованиям, и это все еще предпочтительно для чтения вывода в переменную или массив:

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. Но также нужно избежать в случаях, когда можно просто передать его по каналу к команде, которая может сделать работу, которая является большинством случаев.

1
ответ дан 23 November 2019 в 05:21

Другие вопросы по тегам:

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