Краткое и портативное "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 более ориентирована на поток, поэтому она не такой уж и портативный.