Как объединить несколько строк с именами файлов в одну с пользовательским разделителем?


441

Я хотел бы объединить результат ls -1в одну строку и разделить его с тем, что я хочу.

Есть ли какие-либо стандартные команды Linux, которые я могу использовать для достижения этой цели?

Ответы:


689

Аналогичен самому первому варианту, но пропускает конечный разделитель

ls -1 | paste -sd "," -

29
Как примечание, версия вставки, которую я пробовал, требует в конце аргумента "-", чтобы указать, что он должен читать из STDIN. например, ls -1 | paste -s -d ":" - не уверен, что это универсально со всеми версиями пасты
— Энди Уайт

4
этот лучше, потому что он допускает пустой разделитель :)
— Юра Пурбеев

2
Примечание pasteполучает -(стандартный ввод) по умолчанию, по крайней мере, на моем paste (GNU coreutils) 8.22.
— Федорки 'ТАК прекрати вредить'

1
я только проголосовал за это, и теперь у него те же голоса, что и у выбранного ответа. ЭТО ОТВЕТ. нет
— конечного разделителя

1
Пустой разделитель может быть указан с помощью "\0", так что paste -sd "\0" -работал для меня!
— Брэд Паркс

379

РЕДАКТИРОВАТЬ : просто " ls -m ", если вы хотите, чтобы ваш разделитель был запятой

Ах, сила и простота!

ls -1 | tr '\n' ','

Поменяйте запятую " , " на что хотите. Обратите внимание, что это включает "запятую"


46
+1, но более сложная версия должна обрабатывать последний \ n по-другому
— mouviciel

5
Если имя файла содержит \nв нем, это также заменит это.
— codaddict

3
@ShreevatsaR: он имеет в виду не добавлять в конце "," я верю. вот такls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
— Крис

7
@Chris: вы sedмогли бы быть немного более эффективными с символом конечного маркера:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
— pieman72

2
Использование sedпосле, trкажется, просто для удаления последнего символа кажется неразумным. Я иду сls -1 | tr '\n' ',' | head -c -1
— Reddot

29

Это заменяет последнюю запятую новой строкой:

ls -1 | tr '\n' ',' | sed 's/,$/\n/'

ls -m включает символы новой строки в ширину экрана (например, 80-е).

В основном Bash (только lsвнешне):

saveIFS=$IFS; IFS=$'\n'
files=($(ls -1))
IFS=,
list=${files[*]}
IFS=$saveIFS

Использование readarray(иначе mapfile) в Bash 4:

readarray -t files < <(ls -1)
saveIFS=$IFS
IFS=,
list=${files[*]}
IFS=$saveIFS

Спасибо gniourf_gniourf за предложения.


Это не позаботится о файлах с пробелами в имени. Попробуйте это: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && touch "это файл" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
— Димир

1
@dimir: Многие из ответов на этот вопрос страдают от этой проблемы. Я отредактировал свой ответ, чтобы разрешить имена файлов с табуляцией или пробелами, но не переводы строк.
— Приостановлено до дальнейшего уведомления.

Ваша версия bash также страдает от расширений пути. Чтобы построить массив из строк, пожалуйста , рассмотрите возможность использования mapfile(Bash) ≥4 как: mapfile -t files < <(ls -1). Не надо возиться IFS. И это тоже короче.
— gniourf_gniourf

И когда у вас есть массив, вы можете использовать , IFSчтобы присоединиться поля: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Или используйте другой метод, если вы хотите разделитель с более чем одним символом.
— gniourf_gniourf

1
@gniourf_gniourf: я включил ваши предложения в мой ответ. Спасибо.
— Приостановлено до дальнейшего уведомления.

24

Я думаю, что это потрясающе

ls -1 | awk 'ORS=","'

ORS является «разделителем выходных записей», поэтому теперь ваши строки будут соединяться запятой.


6
Это не исключает завершающий разделитель.
— Дерек Махар

6
Это особенно здорово из-за обработки многосимвольных разделителей записей (например, " OR ")
— Мат Шаффер

15

Комбинация настроек IFSи использования "$*"может делать то, что вы хотите. Я использую подоболочку, поэтому я не вмешиваюсь в $ IFS этой оболочки

(set -- *; IFS=,; echo "$*")

Чтобы захватить вывод,

output=$(set -- *; IFS=,; echo "$*")

2
У вас есть еще информация о том, как setработает? Для меня это выглядит как вуду. поверхностный просмотр man setне принес мне много информации.
— Этеш Чоудхури

3
Если вы даете setкучу аргументов, но без опций, он устанавливает позиционные параметры ($ 1, $ 2, ...). --защищать setв случае, если первый аргумент (или имя файла в этом случае) начинается с тире. Смотрите описание --опции в help set. Я считаю позиционные параметры удобным способом обработки списка вещей. Я мог бы также реализовать это с помощью массива:output=$( files=(*); IFS=,; echo "${files[*]}" )
— glenn jackman

Это замечательно, поскольку не требует запуска каких-либо дополнительных программ и работает с именами файлов, которые содержат пробелы или даже переводы строк.
— Эрик

1
@EhteshChoudhury Как type setскажет вам set is a shell builtin. Так что, man setне поможет, но help setсделаю. Ответ: «- Присвойте все оставшиеся аргументы позиционным параметрам».
— Стефан Гурихон

После а set -- *. Отсрочка расширения *одного уровня вы можете получить правильный вывод без необходимости подпроекта оболочки: IFS=',' eval echo '"$*"'. Конечно, это изменит позиционные параметры.
— Исаак

13

lsВ целом, синтаксический анализ не рекомендуется , поэтому лучше использовать альтернативный способ find, например:

find . -type f -print0 | tr '\0' ','

Или с помощью findи paste:

find . -type f | paste -d, -s

Для общего объединения нескольких строк (не относящихся к файловой системе), проверьте: сжатое и переносимое «соединение» в командной строке Unix .


9

Не изобретай велосипед.

ls -m

Это именно так.


ОП требовал любой разделитель, поэтому вам все равно понадобится tr для преобразования запятых. Он также добавляет пробел после запятых, то есть file1, file2, file3
— rob

так что, используя ls -mи, trчтобы удалить пробел после запятой, вы бы сделалиls -m | tr -d ' '
— Энди

2
что использование tr удалит пробелы внутри имен файлов. лучше использоватьsed 's/, /,/g
— Гленн Джекман

7

просто удар

mystring=$(printf "%s|" *)
echo ${mystring%|}

5
Чуть более эффективным было бы использовать "printf -v mystring"% s | "*", чтобы избежать форка для $ ()
— camh

Но, в частности, не |смущает отставание , @camh.
— Кристофер

1
Ну просто bashгну и coreutilsprintf
— ThorSummoner

@camh Но printf -vбудет работать только в bash, в то время как представленный ответ работает на многих типах оболочек.
— Исаак

@Christopher Да, это приведет к удалению завершающего |, при условии , что используются оба линий: printf -v mystring "%s|" * ; echo ${mystring%|}.
— Исаак

7

Эта команда для поклонников PERL:

ls -1 | perl -l40pe0

Здесь 40 - восьмеричный восьмеричный код для пространства.

-p будет обрабатывать построчно и печатать

-l позаботится о замене завершающего \ n символом ascii, который мы предоставляем.

-e должен сообщить PERL, что мы выполняем командную строку.

0 означает, что на самом деле нет команды для выполнения.

perl -e0 совпадает с perl -e ''


6

Чтобы избежать путаницы с новой строкой для tr, мы можем добавить флаг -b к ls:

ls -1b | tr '\n' ';'

5

Похоже, ответы уже существуют.

Если вы хотите a, b, cформатировать, используйте ls -m( ответ Tulains Córdova )

Или, если вы хотите a b cформатировать, используйте ls | xargs(упрощенная версия ответа Криса J )

Или, если вы хотите любой другой разделитель, как |, используйте ls | paste -sd'|'(применение ответа Артема )


5

Седь,

sed -e ':a; N; $!ba; s/\n/,/g'
  # :a         # label called 'a'
  # N          # append next line into Pattern Space (see info sed)
  # $!ba       # if it's the last line ($) do not (!) jump to (b) label :a (a) - break loop
  # s/\n/,/g   # any substitution you want

Примечание :

Это линейный по сложности, заменяющий только один раз после того, как все строки добавлены в Pattern Space.

@ AnandRajaseka в ответ , и некоторые другие подобные ответы, такие , как здесь , являются O (n²), потому что СЭД должна делать заменить каждый раз , когда новая строка добавляется в шаблон пространства.

Сравнивать,

seq 1 100000 | sed ':a; N; $!ba; s/\n/,/g' | head -c 80
  # linear, in less than 0.1s
seq 1 100000 | sed ':a; /$/N; s/\n/,/; ta' | head -c 80
  # quadratic, hung

5

Добавляя поверх ответа majkinetor, вот способ удаления конечного разделителя (поскольку я пока не могу просто комментировать его ответ):

ls -1 | awk 'ORS=","' | head -c -1

Просто удалите столько конечных байтов, сколько рассчитывает ваш разделитель.

Мне нравится этот подход, потому что я могу использовать многосимвольные разделители + другие преимущества awk:

ls -1 | awk 'ORS=", "' | head -c -2

РЕДАКТИРОВАТЬ

Как заметил Питер, отрицательный счетчик байтов не поддерживается в родной версии головы MacOS. Это, однако, может быть легко исправлено.

Сначала установите coreutils. «Основные утилиты GNU - это базовые утилиты для работы с файлами, оболочками и текстом в операционной системе GNU».

brew install coreutils

Команды, также предоставляемые MacOS, устанавливаются с префиксом «g». Например gls.

После того, как вы это сделаете, вы можете использовать gheadс отрицательным числом байтов или, лучше, сделать псевдоним:

alias head="ghead"

Примечание: отрицательный счетчик байтов поддерживается только в определенных версиях head, поэтому это не сработает, например, в macos.
— Питер

Спасибо что подметил это. Я добавил обходной путь для MacOS.
— Александр Стельмачонек,

3

Если ваша версия xargs поддерживает флаг -d, это должно работать

ls  | xargs -d, -L 1 echo

-d - флаг-разделитель

Если у вас нет -d, вы можете попробовать следующее

ls | xargs -I {} echo {}, | xargs echo

Первый xargs позволяет вам указать разделитель, который в этом примере является запятой.


3
-dзадает входной разделитель с помощью GNU xargs, поэтому не будет работать. Второй пример демонстрирует ту же проблему, что и другие решения здесь, в отношении случайного разделителя в конце.
— Тор

3
sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]

Объяснение:

-e- обозначает команду, которая должна быть выполнена
:a- является меткой
/$/N- определяет область совпадения для текущей и (N) ext строки
s/\n/\\n/;- заменяет все EOL на \n
ta;- идет на метку a, если совпадение прошло успешно

Взято из моего блога .



2

lsпри подключении к каналу выводит один столбец, поэтому он -1является избыточным.

Вот еще один ответ Perl, использующий встроенную joinфункцию, которая не оставляет завершающий разделитель:

ls | perl -F'\n' -0777 -anE 'say join ",", @F'

Непонятный -0777заставляет Perl читать все вводные данные перед запуском программы.

альтернатива sed, которая не оставляет завершающий разделитель

ls | sed '$!s/$/,/' | tr -d '\n'

0

lsимеет возможность -mразделить вывод ", "запятой и пробелом.

ls -m | tr -d ' ' | tr ',' ';'

Если вы передадите этот результат, чтобы trудалить пробел или запятую, вы сможете снова передать результат, trчтобы заменить разделитель.

в моем примере я заменяю разделитель ,разделителем;

замените его ;любым предпочитаемым символом , так как tr учитывает только первый символ в строках, которые вы передаете в качестве аргументов.


0

Вы можете использовать chomp для объединения нескольких строк в одну строку:

perl -e 'while (<>) {if (/ \ $ /) {chomp; } print;} 'bad0> test

поставить условие разрыва строки в операторе if. Это может быть специальный символ или любой разделитель.


0

Версия Quick Perl с обработкой косой черты:

ls -1 | perl -E 'say join ", ", map {chomp; $_} <>'

Объяснить:

  • perl -E: выполнить Perl с поддержкой функций (скажем, ...)
  • скажем: печать с возвратом носителя
  • join ",", ARRAY_HERE: присоединить массив с помощью ","
  • map {chomp; $ _} ROWS: убрать из каждой строки оператор возврата и вернуть результат
  • <>: stdin, каждая строка является строкой, в сочетании с картой она создаст массив каждой строки
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.