Краткое и портативное "join" в командной строке Unix
Как я могу объединить несколько строк в одну строку, с разделителем, где были символы новой строки, и избегая конечного разделителя и, возможно, игнорируя пустые строки?
Пример. Рассмотрим текстовый файл foo.txt
, состоящий из трех строк:
foo
bar
baz
Желаемый результат:
foo,bar,baz
Команда, которую я использую сейчас:
tr 'n' ',' <foo.txt |sed 's/,$//g'
В идеале это было бы примерно так:
cat foo.txt |join ,
Что такое:
- самый портативный, краткий, читаемый способ.
- в самый лаконичный способ с использованием нестандартных unix инструментов.
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!
выполнение команды viq!
, которая в основном завершается без сохранения (-s
- это отключение вывода).ex
эквивалентноvi -e
.Этот метод является довольно портативным, так как большинство Linux / Unix поставляются с
ex
/vi
по умолчанию. И это более совместимо, чем использованиеsed
, где параметр in-place (-i
) не является стандартным расширением, а утилита IT-self более ориентирована на поток, поэтому она не такой уж и портативный.