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


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.