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


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знятим прапором) дає результат, ідентичний вхідному.


Чи потрібні роздільники пробілів у комах, або прийнятна лише кома?
steeldriver

Або добре, але мені потрібен символ цитати, що оточує рядок, 'або ".
fbt


Я перший, хто помітив, що вхідні дані та сценарій для їх обробки є абсолютно несумісними. Виходу не буде.
ctrl-alt-delor

Перелічений я сценарій - це те, як я генерую вхідні дані. Хтось просив цього. Фактичні вхідні дані будуть виглядати приблизно так це . Зауважте, що Github змінює форматування, щоб видалити нові рядки.
fbt

Відповіді:


19

Ви можете додати лапки з sed, а потім об'єднати лінії з пастою :

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

Якщо ви використовуєте систему на основі GNU coreutils (тобто Linux), ви можете опустити трейлінг '-'.

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

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

1
На MacOS (а може бути й інших) вам потрібно буде включити тире, щоб вказати, що вхід є з stdin, а не з файлу:sed 's/^\|$/"/g'|paste -sd, -
cherdt

Правда, версія "coreutils" вставки прийме обидві форми, але "-" - це більше POSIX. Дякую !
зеппелін

2
Або просто sedнаодинці:sed 's/.*/"&"/;:l;N;s/\n\(.*\)$/, "\1"/;tl'
Цифрова травма

1
@fbt Примітка, яку я зараз додав наприкінці своєї відповіді, стосується і тут.
phk

1
@DigitalTrauma - не дуже гарна ідея; це буде дуже повільно (може навіть зависати з величезними файлами) - дивіться відповіді на QI, пов'язані в моєму коментарі до питання 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кожен запис (або в- awkSpeak: запис ) є префіксом та додатково друкується з єдиними цитатами навколо нього. awkМінлива роздільник вихідних записів ORSне потрібно (так як префікс робить це для вас) , тому він встановлений бути порожнім в BEGINИнж. О, і ми можемо наш файл ENDз новим рядком (наприклад, це працює з подальшими інструментами для обробки тексту); якщо для цього не потрібна частина, ENDі все після неї (всередині одинарних лапок) можна буде видалити.

Примітка

Якщо у вас є закінчення рядків у стилі Windows / DOS ( \r\n), спочатку їх потрібно перетворити у стиль UNIX ( \n). Для цього ви можете поставити tr -d '\015'на початку свого трубопроводу:

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

(Припустимо, \rщо у вашому файлі немає жодного використання для s. Тут дуже безпечне припущення.)

Крім того, просто запустіть dos2unix /path/to/input.listодин раз, щоб перетворити файл на місце.


Коли я запускаю цю команду, я отримую ', 'stringr23aphicsяк вихід.
fbt

@fbt Дивіться мою останню примітку.
phk

2
print p"'"'"'"$0"'"'"'"; p=", "—Добре цитати, Бетмен!
wchargin

Я знаю, правда :) Я думав про те, щоб згадати, що в багатьох оболонках друк p"'\''"$0"'\''";також працював би (це не POSIXy, хоч), або альтернативно використовувати bashрядки C з цитуванням ( $'') навіть просто print p"\'"$0"\'";(можливо, зажадає подвоєння інших наклонів), але є вже awkвтікає інший метод, що використовує символи 's.
фк

Нічого собі, я не можу повірити, що ти це зрозумів. Дякую.
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(", "); }

Але якщо прийнятна просто гідна продуктивність (і якщо ви не проводите стрес-тест, ви не зможете виміряти будь-які різниці між постійними факторами, вони всі миттєві), і ви хочете отримати гнучкість з роздільниками і розумну -лайн-у-ніс,

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

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


редагувати: що межує з абсурдом, досить легко отримати flex, щоб обіграти все інше порожне, просто скажіть 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 разів швидші за все.


1
(paste -d\ \'\' /dev/null /dev/null - /dev/null | paste -sd, -) <infile | cut -c2-зробила б кома + пробіл @ майже таку ж швидкість, хоча, як ви зазначили, це не дуже гнучко, якщо вам потрібна якась фантазія, як роздільник
don_crissti

Цей flexматеріал є досить проклятим класною людиною ... це вперше я бачу, як хтось flexрозміщує код на цьому сайті ... великий підсумок! Будь ласка, опублікуйте більше цього матеріалу.
don_crissti

@don_crissti Дякую! Я буду шукати хороших можливостей, sed / awk / whatnot - це, як правило, кращі варіанти лише для зручності, але часто є досить легкий відповідь.
jthill

4

Perl

Пітон однолінійний:

$ 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 Так, за винятком бажаного виводу ОП - вони хочуть, щоб кожне слово було цитованим, і нам потрібно видалити останні рядки, щоб забезпечити вихід одного рядка. У вас є ідея, як це зробити без розуміння списку?
Сергій Колодяжний

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
Приємне рішення. Але хоча ОП явно не просив, 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 '...'

--Паул


2
Ласкаво просимо на Unix.stackexchange! Я рекомендую вам взяти тур .
Стівен Рауч

0

Смішно, що для використання простого списку текстових пакетів R, щоб встановити їх у R, ніхто не запропонував рішення, використовуючи цей список безпосередньо в R, але боротися з bash, perl, python, awk, sed або будь-яким іншим, щоб поставити лапки і коми в список. Це взагалі не потрібно, і більше того, не вирішує спосіб введення та використання перетвореного списку в Р.

Ви можете просто завантажити звичайний текстовий файл (сказав, 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.