Превращение отдельных строк в разделенный запятыми список с цитируемыми записями

15

У меня есть следующие данные (список пакетов R, проанализированных из файла Rmarkdown), которые я хочу превратить в список, который я могу передать R для установки:

d3heatmap
data.table
ggplot2
htmltools
htmlwidgets
metricsgraphics
networkD3
plotly
reshape2
scales
stringr

Я хочу превратить список в список формы:

'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'

В настоящее время у меня есть конвейер bash, который идет от необработанного файла к списку выше:

grep 'library(' Presentation.Rmd \
| grep -v '#' \
| cut -f2 -d\( \
| tr -d ')'  \
| sort | uniq

Я хочу добавить шаг, чтобы включить новые строки в список через запятую. Я попытался добавить tr '\n' '","', что не удается. Я также попробовал ряд следующих ответов переполнения стека, которые также не дают результатов:

Это производит library(stringr)))phics)в результате.

Это производит ,%в результате.

Этот ответ (со -iснятым флагом) производит вывод, идентичный вводу.

FBT
источник
Должны ли разделители быть запятыми или только одна запятая?
SteelDriver
Либо это хорошо, но мне нужна кавычка окружающей строки, либо 'или ".
FBT
Я первый, кто заметил, что входные данные и скрипт для их обработки совершенно несовместимы. Там не будет никакого выхода.
ctrl-alt-delor
Сценарий, который я перечислил, - это то, как я генерирую входные данные. Кто-то просил об этом. Фактические входные данные будут выглядеть примерно так это . Обратите внимание, что Github изменяет форматирование, чтобы удалить новые строки.
17

Ответы:

19

Вы можете добавить кавычки с помощью sed, а затем объединить строки со вставкой , например:

sed 's/^\|$/"/g'|paste -sd, -

Если вы используете систему на основе GNU coreutils (т.е. Linux), вы можете опустить трейлинг '-'.

Если введенные данные имеют окончания строки в стиле DOS (как предложено @phk), вы можете изменить команду следующим образом:

sed 's/\r//;s/^\|$/"/g'|paste -sd, -
дирижабль
источник
1
В MacOS (и, возможно, в других) вам нужно будет добавить тире, чтобы указать, что входные данные sed 's/^\|$/"/g'|paste -sd, -
взяты
Правда, версия пасты "coreutils" будет принимать обе формы, но "-" больше POSIX. Спасибо !
Цеппелин
2
Или только с sedодним:sed 's/.*/"&"/;:l;N;s/\n\(.*\)$/, "\1"/;tl'
Цифровая травма
1
@fbt Примечание, добавленное мной в конце моего ответа, применимо и здесь.
PHK
1
@DigitalTrauma - не очень хорошая идея; это будет очень медленно (может даже зависать с огромными файлами) - смотрите ответы на вопрос о ссылках в моем комментарии к Q здесь; классная вещь - использовать pasteодин;)
don_crissti
8
Использование awk:
awk 'BEGIN { ORS="" } { print p"'"'"'"$0"'"'"'"; p=", " } END { print "\n" }' /path/to/list
Альтернатива с меньшим количеством экранирования и, следовательно, более читабельным:
awk 'BEGIN { ORS="" } { print p"\047"$0"\047"; p=", " } END { print "\n" }' /path/to/list
Выход:
'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'
Объяснение:

Сам awkскрипт без всякого побега есть BEGIN { ORS="" } { print p"'"$0"'"; p=", " } END { print "\n" }. После печати первой записи переменная pустанавливается (до этого она похожа на пустую строку). С этой переменной pкаждая запись (или в awk-speak: record ) имеет префикс и дополнительно печатается в одинарных кавычках. awkПеременная разделитель выходных записей ORSне требуется (так как префикс делает это для вас) , поэтому он установлен быть пустым в BEGINИнж. Да, и мы можем отправить наш файл ENDс новой строкой (например, чтобы он работал с другими инструментами обработки текста); если в этом нет необходимости, часть с ENDи все после нее (внутри одинарных кавычек) можно удалить.

Заметка

Если у вас есть окончания строк в стиле Windows / DOS ( \r\n), вы должны \nсначала преобразовать их в стиль UNIX ( ). Для этого вы можете поместить tr -d '\015'в начало вашего конвейера:

tr -d '\015' < /path/to/input.list | awk […] > /path/to/output

(Предполагая, что вы не используете \rs в вашем файле. Очень безопасное предположение здесь.)

Или просто запустите dos2unix /path/to/input.listодин раз, чтобы преобразовать файл на месте.

PHK
источник
Когда я запускаю эту команду, я получаю ', 'stringr23aphicsв качестве вывода.
FBT
@fbt Смотрите мою последнюю заметку.
PHK
2
print p"'"'"'"$0"'"'"'"; p=", "- святые цитаты, Бэтмен!
wchargin
Я знаю, верно… :) Я подумал упомянуть, что во многих оболочках печать p"'\''"$0"'\''";также работала бы (хотя это не POSIXy), или, альтернативно, использовала бы bashстроки цитирования C ( $'') даже просто print p"\'"$0"\'";( хотя, возможно, потребовалось бы удвоить другие обратные слэши), но есть уже другой метод, использующий awkсимвол `ы '.
PhK
Вау, я не могу поверить, что ты понял это. Спасибо.
FBT
6

Как показывает связанный ответ @ don_crissti , опция вставки граничит с невероятно быстрой скоростью - конвейер ядра Linux более эффективен, чем я мог бы поверить, если бы не попробовал его сейчас. Примечательно, что если вас устраивает одна запятая, разделяющая элементы списка, а не запятая + пробел, конвейер вставки

(paste -d\' /dev/null - /dev/null | paste -sd, -) <input

быстрее, чем даже разумная flexпрограмма (!)

%option 8bit main fast
%%
.*  { printf("'%s'",yytext); }
\n/(.|\n) { printf(", "); }

Но если приемлема только приличная производительность (и если вы не проводите стресс-тестирование, вы не сможете измерить различия с постоянными коэффициентами, они все мгновенные), и вам нужны как гибкость с вашими разделителями, так и разумная. -liner-у-ность,

sed "s/.*/'&'/;H;1h;"'$!d;x;s/\n/, /g'

это твой билет Да, это похоже на линейный шум, но H;1h;$!d;xидиома - это правильный способ заморозить все, как только вы поймете, что все становится действительно легко читаемым, за s/.*/'&'/ним следуют глухой и a s/\n/, /g.


редактирование: граничит с абсурдом, довольно легко добиться гибкости, чтобы превзойти все остальное, просто скажите stdio, что вам не нужна встроенная синхронизация многопоточной обработки / обработки сигналов:

%option 8bit main fast
%%
.+  { putchar_unlocked('\'');
      fwrite_unlocked(yytext,yyleng,1,stdout);
      putchar_unlocked('\''); }
\n/(.|\n) { fwrite_unlocked(", ",2,1,stdout); }

и в состоянии стресса это в 2-3 раза быстрее, чем конвейеры пасты, которые сами по крайней мере в 5 раз быстрее, чем все остальное.

jthill
источник
1
(paste -d\ \'\' /dev/null /dev/null - /dev/null | paste -sd, -) <infile | cut -c2-будет делать запятую + пробел @ почти с той же скоростью, хотя, как вы заметили, она не очень гибкая, если вам нужна какая-то
необычная
Это flexчертовски крутой человек ... это первый раз, когда я вижу, как кто-то публикует flexкод на этом сайте ... большое upvote! Пожалуйста, опубликуйте больше этого материала.
don_crissti
@don_crissti Спасибо! Я буду искать хорошие возможности, sed / awk / whatnot, как правило, лучшие варианты только для удобства, но часто есть и довольно гибкий ответ.
до
4

Perl

Python однострочный:

$ python -c "import sys; print ','.join([repr(l.strip()) for l in sys.stdin])" < input.txt                               
'd3heatmap','data.table','ggplot2','htmltools','htmlwidgets','metricsgraphics','networkD3','plotly','reshape2','scales','stringr'

Работает просто - мы перенаправляем input.txt в stdin, используя <оператор оболочки , читаем каждую строку в список, .strip()удаляя символы новой строки и repr()создавая представление каждой строки в кавычках. Затем список объединяется в одну большую строку через .join()функцию с ,разделителем.

В качестве альтернативы мы могли бы использовать +конкатенацию кавычек для каждой зачистки.

 python -c "import sys;sq='\'';print ','.join([sq+l.strip()+sq for l in sys.stdin])" < input.txt

Perl

По сути, та же идея, что и раньше: читать все строки, зачеркивать завершающий символ новой строки, заключать в одинарные кавычки, помещать все в массив @cvs и выводить значения массива, объединенные запятыми.

$ perl -ne 'chomp; $sq = "\047" ; push @cvs,"$sq$_$sq";END{ print join(",",@cvs)   }'  input.txt                        

'D3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'весы', 'stringr'

Сергей Колодяжный
источник
IIRC, питоны joinдолжны иметь возможность взять итератор, поэтому не должно быть необходимости материализовать цикл stdin в список
iruvar
@iruvar Да, кроме как посмотреть на желаемый вывод OP - они хотят, чтобы каждое слово было заключено в кавычки, и нам нужно удалить завершающие символы новой строки, чтобы обеспечить вывод одной строкой. У вас есть идея, как сделать это без понимания списка?
Сергей Колодяжный
3

Я думаю, что следующее должно быть хорошо, если вы данные в тексте файла

d3heatmap
data.table
ggplot2
htmltools
htmlwidgets
metricsgraphics
networkD3
plotly
reshape2
scales
stringr

Давайте использовать массивы, которые имеют подстановку внизу:

#!/bin/bash
input=( $(cat text) ) 
output=( $(
for i in ${input[@]}
        do
        echo -ne "'$i',"
done
) )
output=${output:0:-1}
echo ${output//,/, }

Вывод скрипта должен быть следующим:

'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'

Я полагаю, это было то, что вы искали?

Шарль ван дер Генугтен
источник
1
Хорошее решение. Но хотя OP явно не просил bashи хотя можно с уверенностью предположить, что кто-то может его использовать (в конце концов, AFAIK - это наиболее используемая оболочка), его все равно не следует воспринимать как должное. Кроме того, есть части, которые вы могли бы лучше использовать при цитировании (вставляя двойные кавычки). Например, несмотря на то, что в именах пакетов вряд ли есть пробелы, по-прежнему целесообразно заключать в кавычки переменные, а не нет, вы можете захотеть запустить shellcheck.net и посмотреть там примечания и пояснения.
PHK
2

У меня часто бывает очень похожий сценарий: я копирую столбец из Excel и хочу преобразовать содержимое в список через запятую (для последующего использования в запросе SQL, например ... WHERE col_name IN <comma-separated-list-here>).

Это то, что у меня есть в моем .bashrc:

function lbl {
    TMPFILE=$(mktemp)
    cat $1 > $TMPFILE
    dos2unix $TMPFILE
    (echo "("; cat $TMPFILE; echo ")") | tr '\n' ',' | sed -e 's/(,/(/' -e 's/,)/)/' -e 's/),/)/'
    rm $TMPFILE
}

Затем я запускаю lbl(«строка за строкой») в строке cmd, которая ожидает ввода, вставляю содержимое из буфера обмена, нажимаю, <C-D>и функция возвращает ввод, окруженный (). Это выглядит так:

$ lbl
1
2
3
dos2unix: converting file /tmp/tmp.OGM6UahLTE to Unix format ...
(1,2,3)

(Я не помню, почему я поместил dos2unix здесь, предположительно, потому что это часто вызывает проблемы в настройке моей компании.)

Рольф
источник
1

Некоторые версии sed ведут себя немного иначе, но на моем mac я могу обрабатывать все, кроме «uniq» в sed:

sed -n -e '
# Skip commented library lines
/#/b
# Handle library lines
/library(/{
    # Replace line with just quoted filename and comma
    # Extra quoting is due to command-line use of a quote
    s/library(\([^)]*\))/'\''\1'\'', /
    # Exchange with hold, append new entry, remove the new-line
    x; G; s/\n//
    ${
        # If last line, remove trailing comma, print, quit
        s/, $//; p; b
    }
    # Save into hold
    x
}
${
    # Last line not library
    # Exchange with hold, remove trailing comma, print
    x; s/, $//; p
}
'

К сожалению, чтобы исправить уникальную часть, вы должны сделать что-то вроде:

grep library Presentation.md | sort -u | sed -n -e '...'

--Павел

PaulC
источник
2
Добро пожаловать в Unix.stackexchange! Я рекомендую вам взять тур .
Стивен Раух
0

Забавно, что если использовать простой текстовый список пакетов R для их установки в R, никто не предлагал решения, использующего этот список непосредственно в R, а боролся с bash, perl, python, awk, sed или чем-то другим, чтобы поставить кавычки и запятые в список. Это вовсе не обязательно и, более того, не решает, как вводить и использовать преобразованный список в R.

Вы можете просто загрузить простой текстовый файл (скажем, packages.txt) в виде фрейма данных с одной переменной, которую вы можете извлечь как вектор, непосредственно используемый для install.packages. Итак, преобразуйте его в полезный объект R и установите этот список просто:

df <- read.delim("packages.txt", header=F, strip.white=T, stringsAsFactors=F)
install.packages(df$V1)

Или без внешнего файла:

packages <-" 
d3heatmap
data.table
ggplot2
htmltools
htmlwidgets
metricsgraphics
networkD3
plotly
reshape2
scales
stringr
"
df <- read.delim(textConnection(packages), 
header=F, strip.white=T, stringsAsFactors=F)
install.packages(df$V1)
Fran
источник