将单独的行变成带引号的逗号分隔列表


15

我有以下数据(从Rmarkdown文件解析的R软件包列表),我想将其转换为可传递给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 '17

两种都可以,但我确实需要在字符串两边加上引号,'或者"
fbt


我是第一个注意到输入数据和处理它的脚本完全不兼容的人吗?将没有输出。
ctrl-alt-delor

我列出的脚本是如何生成输入数据的。有人要。实际的输入数据看起来像这样。请注意,Github更改了格式以删除新行。
fbt

Answers:


19

您可以使用sed添加引号,然后使用paste合并行,如下所示:

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

如果您正在运行基于GNU coreutils的系统(即Linux),则可以省略尾随'-'

如果您输入的数据具有DOS样式的行尾(如@phk所建议),则可以如下修改命令:

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

1
在MacOS(可能还有其他的MacOS)上,您需要添加破折号以指示输入来自标准输入而不是文件:sed 's/^\|$/"/g'|paste -sd, -
cherdt

的确,粘贴的“ coreutils”版本将接受两种形式,但“-”则更像POSIX。谢谢 !
Zeppelin

2
或仅与sed一个人在一起:sed 's/.*/"&"/;:l;N;s/\n\(.*\)$/, "\1"/;tl'
Digital Trauma '01

1
@fbt我现在在答案末尾添加的注释也适用于此。
phk

1
@DigitalTrauma-不是一个好主意;那将非常慢(甚至可能挂起大文件)-在此处查看我对Q的评论中链接的QI的答案;最酷的事情是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不需要(因为前缀是干嘛的你),所以它被设置为在空BEGINING。哦,我们可能会将文件添加到END换行符(例如,以便与其他文本处理工具一起使用);如果不需要的话,END可以删除该部分及其后的所有内容(单引号内)。

注意

如果您使用Windows / DOS风格的行尾(\r\n),则必须\n先将其转换为UNIX风格()。为此,您可以将其放在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

我知道,对吧‽:)我想到要提到的是,在许多shell中,打印p"'\''"$0"'\''";也可以工作(虽然不是POSIXy),或者甚至可以使用bashC引用字符串($''print p"\'"$0"\'";(尽管可能需要加倍其他反斜杠),但是已经使用awk的字符转义的另一种方法。
phk

哇,我简直不敢相信你明白这一点。谢谢。
fbt

6

正如@don_crissti的链接答案所示,paste选项的使用速度非常快-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-会使用逗号+空格@几乎以相同的速度运行,尽管正如您指出的那样,如果您需要一些
特殊的

这些flex东西真是太该死了……这是我第一次看到有人flex在这个网站上发布代码……大赞!请发布更多这些东西。
don_crissti

@don_crissti谢谢!我会寻找好的机会,sed / awk / whatnot通常只是为了方便起见,是更好的选择,但通常也有一个非常简单的flex答案。
jthill

4

佩尔

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'

以简单的方式工作-我们使用shell的<运算符将input.txt重定向到stdin ,将每行读入列表,其中.strip()删除换行符并repr()为每行创建带引号的表示形式。然后通过.join()函数将列表,作为分隔符连接成一个大字符串

另外,我们也可以+将引号连接到每条被删除的行。

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

佩尔

本质上与以前的想法相同:读取所有行,将尾随换行符分隔开,用单引号引起来,将所有内容填充到数组@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','scales','stringr'


IIRC,python join应当能够使用迭代器,因此应该不需要将stdin循环
具体化为

@iruvar是的,除了查看OP的期望输出-他们希望每个单词都用引号引起来,我们需要删除尾随的换行符以确保输出为一行。您有一个想法,没有列表理解怎么办?
Sergiy Kolodyazhnyy

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是最常用的shell),但仍然不应将其视为理所当然。另外,有些地方可能会使报价更好(用双引号引起来)。例如,尽管软件包名称中不太可能包含空格,但还是习惯于用引号而不是变量来代替,您可能希望在其上运行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上,我可以处理sed中的“ uniq”以外的所有内容:

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!我建议您参加游览
Stephen Rauch

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.