Краткий и переносимый "join" в командной строке Unix

Как я могу объединить несколько строк в одну строку с разделителем, где были символы новой строки, и избегать трейлинг-разделителя и, необязательно, игнорировать пустые строки?

Пример. Рассмотрим текстовый файл foo.txt с тремя строками:

foo
bar
baz

Требуемый результат:

foo,bar,baz

Команда, которую я сейчас использую:

tr '\n' ',' <foo.txt |sed 's/,$//g'

В идеале это будет примерно так:

cat foo.txt |join ,

Что:

  • самый портативный, лаконичный, читаемый способ.
  • наиболее сжатый способ использования нестандартных инструментов unix.

Конечно, я мог бы написать что-нибудь или просто использовать псевдоним. Но мне интересно знать варианты.

Ответ 1

Возможно, немного удивительно, paste - хороший способ сделать это:

paste -s -d","

Это не касается пустых строк, которые вы упомянули. Для этого проведите текст через grep, сначала:

grep -v '^$' | paste -s -d"," -

Ответ 2

Эта sed одна строка должна работать -

sed -e :a -e 'N;s/\n/,/;ba' file

Тест:

[jaypal:~/Temp] cat file
foo
bar
baz

[jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file
foo,bar,baz

Чтобы обрабатывать пустые строки, вы можете удалить пустые строки и перенести их на вышеупомянутый однострочный.

sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba'

Ответ 3

Как использовать xargs?

для вашего случая

$ cat foo.txt | sed 's/$/, /' | xargs

Соблюдайте предельную длину ввода команды xargs. (Это означает, что очень длинный входной файл не может быть обработан этим.)

Ответ 4

Perl:

cat data.txt | perl -pe 'if(!eof){chomp;$_.=","}'

но все же короче и быстрее, удивительно:

cat data.txt | perl -pe 'if(!eof){s/\n/,/}'

или, если хотите:

cat data.txt | perl -pe 's/\n/,/ unless eof'

Ответ 5

Просто для удовольствия, здесь решение all-builtins

IFS=$'\n' read -r -d '' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )

Вы можете использовать printf вместо echo, если конечная новая строка является проблемой.

Это работает, установив IFS, разделители, которые read будут разделены, только на новую строку, а не на другие пробелы, а затем сообщают read не прекращать чтение, пока не достигнут nul, вместо новой строки он обычно использует и добавляет каждый элемент, считываемый в данные массива (-a). Затем в подоболочке, чтобы не сжимать IFS интерактивной оболочки, мы устанавливаем IFS в , и расширяем массив с помощью *, который ограничивает каждый элемент массива первым символом в IFS

Ответ 6

Мне нужно было сделать что-то подобное, распечатав список полей, разделенных запятыми, из файла, и был доволен тем, что STDOUT для труб был xargs и ruby, например:

cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs ruby -e "puts ARGV.join(', ')"

Ответ 7

Простой способ соединения строк с пространством на месте с помощью ex (также игнорируя пустые строки), используйте:

ex +%j -cwq foo.txt

Если вы хотите распечатать результаты на стандартный вывод, попробуйте:

ex +%j +%p -scq! foo.txt

Чтобы объединить строки без пробелов, используйте +%j! вместо +%j.

Чтобы использовать другой разделитель, это немного сложнее:

ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt

где g/^$/d (или v/\S/d) удаляет пустые строки, а s/\n/_/ - подстановка, которая в основном работает так же, как и с помощью sed, но для всех строк (%). Когда выполняется синтаксический анализ, напечатайте буфер (%p). И, наконец, -cq! выполнение команды vi q!, которая в основном завершает работу без сохранения (-s - отключить вывод).

Обратите внимание, что ex эквивалентно vi -e.

Этот метод довольно переносимый, поскольку большинство Linux/Unix по умолчанию поставляется с ex/vi. И он более совместим, чем с использованием sed, где параметр места (-i) не является стандартным расширением, а утилита it-self более ориентирована на поток, поэтому она не настолько портативна.

Ответ 8

У меня был файл журнала, в котором некоторые данные были разбиты на несколько строк. Когда это произошло, последним символом первой строки была точка с запятой (;). Я присоединился к этим строкам, используя следующие команды:

for LINE in 'cat $FILE | tr -s " " "|"'
do
    if [ $(echo $LINE | egrep ";$") ]
    then
        echo "$LINE\c" | tr -s "|" " " >> $MYFILE
    else
        echo "$LINE" | tr -s "|" " " >> $MYFILE
    fi
done

Результатом является файл, в котором строки, которые были разделены в файле журнала, были одной строкой в ​​моем новом файле.

Ответ 9

Отвечаю:

awk '{printf "%s", ","$0}' foo.txt

printf достаточно. Нам не нужно -F"\n" менять разделитель полей.