Transformando linhas separadas em uma lista separada por vírgula com entradas entre aspas

13

Eu tenho os seguintes dados (uma lista de pacotes R analisados a partir de um arquivo Rmarkdown), que eu quero transformar em uma lista que posso passar para o R para instalar:

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

Eu quero transformar a lista em uma lista do formulário:

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

Atualmente, tenho um pipeline básico que vai do arquivo bruto para a lista acima:

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

Eu quero adicionar uma etapa para transformar as novas linhas na lista separada por vírgulas. Eu tentei adicionar tr '\n' '","' , que falha. Também experimentei algumas das seguintes respostas do Stack Overflow, que também falham:

Isso produz library(stringr)))phics) como resultado.

Isso produz ,% como resultado.

Esta resposta (com o -i flag removido) produz saída idêntica à entrada.

    
por fbt 17.01.2017 / 19:24

8 respostas

18

Você pode adicionar aspas com sed e, em seguida, mesclar as linhas com colar , assim:

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

Se você estiver executando um sistema GNU baseado em coreutils (ou seja, Linux), você pode omitir o '-' .

Se os dados de entrada tiverem finais de linha no estilo DOS (como @phk sugerido), você poderá modificar o comando da seguinte forma:

sed 's/\r//;s/^\|$/"/g'|paste -sd, -
    
por 17.01.2017 / 19:56
8
Usando awk :
awk 'BEGIN { ORS="" } { print p"'"'"'"$0"'"'"'"; p=", " } END { print "\n" }' /path/to/list
Alternativa com menos escape de shell e, portanto, mais legível:
awk 'BEGIN { ORS="" } { print p"7"$0"7"; p=", " } END { print "\n" }' /path/to/list
Saída:
'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'
Explicação:

O script awk sem todo o escape é BEGIN { ORS="" } { print p"'"$0"'"; p=", " } END { print "\n" } . Depois de imprimir a primeira entrada, a variável p é definida (antes disso, é como uma string vazia). Com esta variável p cada entrada (ou em awk -speak: registro ) é prefixada e adicionalmente impressa com aspas simples ao redor dela. A variável do separador de registro awk de saída ORS não é necessária (já que o prefixo está fazendo isso para você), portanto, ela está configurada para estar vazia na BEGIN ing. Ah, e podemos arquivar para END com uma nova linha (por exemplo, para trabalhar com outras ferramentas de processamento de texto); Se isso não for necessário, a parte com END e tudo depois dela (dentro das aspas simples) pode ser removida.

Nota

Se você tiver finais de linha no estilo Windows / DOS ( \r\n ), será necessário convertê-los para o estilo UNIX ( \n ) primeiro. Para fazer isso, você pode colocar tr -d '5' no início do seu pipeline:

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

(Assumindo que você não tem nenhum uso para \r s no seu arquivo. Assunção muito segura aqui.)

Como alternativa, basta executar dos2unix /path/to/input.list uma vez para converter o arquivo no local.

    
por 17.01.2017 / 19:42
6

Como resposta vinculada do @ don_crissti mostra, a opção de colar faz bordas incrivelmente rápidas - a tubulação do kernel do Linux é mais eficiente do que eu teria acreditado se não tivesse tentado agora. Notavelmente, se você pode ser feliz com uma única vírgula separando seus itens de lista em vez de uma vírgula + espaço, um pipeline de pasta

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

é mais rápido do que um programa flex razoável (!)

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

Mas se apenas o desempenho decente for aceitável (e se você não estiver executando um teste de estresse, não conseguirá medir as diferenças de fator de constante, eles são todos instantâneos) e você quer flexibilidade com seus separadores e razoável one-liner-y-ness,

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

é o seu bilhete. Sim, parece um ruído de linha, mas o idioma H;1h;$!d;x é o jeito certo de aproveitar tudo, assim que você consegue reconhecer que a coisa toda fica fácil de ler, é s/.*/'&'/ seguido por um slurp e s/\n/, /g .

edite: beirando o absurdo, é bastante fácil conseguir que o flex bata todo o resto, apenas diga ao stdio que você não precisa do sync multithread / signalhandler embutido:

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

e sob estresse que é 2-3x mais rápido que os pipelines de pasta, que são eles próprios pelo menos 5x mais rápidos do que todo o resto.

    
por 18.01.2017 / 08:40
4

Perl

Python one-liner:

$ 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'

Funciona de maneira simples - redirecionamos input.txt para stdin usando o operador < do shell, leia cada linha em uma lista com .strip() removendo novas linhas e repr() criando uma representação entre aspas de cada linha. A lista é então unida em uma grande string via .join() function, com , como separador

Como alternativa, poderíamos usar + para concatenar aspas para cada linha separada.

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

Perl

Essencialmente a mesma ideia de antes: leia todas as linhas, retire a nova linha, inclua aspas simples, insira tudo em array @cvs e imprima os valores das matrizes unidos com vírgulas.

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

'd3heatmap', 'data.table', 'ggplot2', 'ferramentas html', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'escalas', 'stringr'

    
por 18.01.2017 / 02:53
3

Acho que o seguinte deve funcionar bem, supondo que você esteja com dados no texto do arquivo

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

Vamos usar matrizes que tenham a substituição como fria:

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

A saída do script deve ser a seguinte:

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

Eu acredito que isso era o que você estava procurando?

    
por 19.01.2017 / 22:41
2

Muitas vezes tenho um cenário muito semelhante: copio uma coluna do Excel e quero converter o conteúdo em uma lista separada por vírgulas (para uso posterior em uma consulta SQL como ... WHERE col_name IN <comma-separated-list-here> ).

Isto é o que eu tenho no meu .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
}

Eu, então, executo lbl ("linha por linha") na linha cmd que espera por entrada, cole o conteúdo da área de transferência, pressione <C-D> e a função retorna a entrada com () . Isso parece assim:

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

(Não me lembro por que coloquei o dos2unix aqui, presumivelmente porque isso costuma causar problemas na configuração da minha empresa).

    
por 24.01.2017 / 10:01
1

Algumas versões do sed são um pouco diferentes, mas no meu mac eu consigo lidar com tudo menos o "uniq" no 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(\([^)]*\))/'\'''\'', /
    # 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
}
'

Infelizmente, para corrigir a parte exclusiva, você precisa fazer algo como:

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

- Paul

    
por 18.01.2017 / 07:40
0

É engraçado que para usar uma lista de texto simples de pacotes R para instalá-los em R, ninguém propôs uma solução usando essa lista diretamente em R, mas lutar com bash, perl, python, awk, sed ou qualquer coisa para colocar aspas e vírgulas na lista. Isso não é necessário e, além disso, não resolve como inserir e usar a lista transformada em R.

Você pode simplesmente carregar o arquivo de texto simples (dito, packages.txt ) como um dataframe com uma única variável, que você pode extrair como um vetor, diretamente utilizável por install.packages . Então, converta-o em um objeto R utilizável e instale essa lista apenas:

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

Ou sem um arquivo externo:

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)
    
por 10.10.2018 / 02:57

Tags