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


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

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

foo
bar
baz

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

foo,bar,baz

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

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

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

cat foo.txt |join ,

Что такое:

  1. самый портативный, краткий, читаемый способ.
  2. в самый лаконичный способ с использованием нестандартных unix инструментов.
Конечно, я мог бы написать что-нибудь или просто использовать псевдоним. Но мне интересно знать варианты.
9   65  

9 ответов:

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

paste -s -d","

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

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

Эта 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'

Как насчет использования xargs?

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

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

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

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'

Просто для удовольствия, вот полностью встроенное решение

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

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

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

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

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

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

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

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

Простой способ соединить строки с пробелом на месте с помощью 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 , где параметр in-place (-i) не является стандартным расширением, а утилита IT-self более ориентирована на поток, поэтому она не такой уж и портативный.

Мой ответ:

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

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