将单独的行转换为带引号条目的逗号分隔列表

fbt*_*fbt 21 linux sed csv tr

我有以下数据(从 Rmarkdown 文件解析的 R 包列表),我想将其转换为可以传递给 R 进行安装的列表:

d3heatmap
data.table
ggplot2
htmltools
htmlwidgets
metricsgraphics
networkD3
plotly
reshape2
scales
stringr
Run Code Online (Sandbox Code Playgroud)

我想将列表转换为以下形式的列表:

'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'
Run Code Online (Sandbox Code Playgroud)

我目前有一个从原始文件到上面列表的 bash 管道:

grep 'library(' Presentation.Rmd \
| grep -v '#' \
| cut -f2 -d\( \
| tr -d ')'  \
| sort | uniq
Run Code Online (Sandbox Code Playgroud)

我想添加一个步骤以将新行转换为逗号分隔列表。我试过添加tr '\n' '","',但失败了。我还尝试了以下 Stack Overflow 的一些答案,但也都失败了:

这产生library(stringr)))phics)了结果。

这产生,%了结果。

这个答案(-i去除了标志)产生与输入相同的输出。

zep*_*lin 28

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

sed 's/^\|$/"/g'|paste -sd, -
Run Code Online (Sandbox Code Playgroud)

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

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

sed 's/\r//;s/^\|$/"/g'|paste -sd, -
Run Code Online (Sandbox Code Playgroud)

  • 在 MacOS(也可能是其他操作系统)上,您需要包含一个破折号来表示输入来自 stdin 而不是文件:`sed 's/^\|$/"/g'|paste -sd, -` (2认同)
  • 或者只使用 `sed`:`sed 's/.*/"&"/;:l;N;s/\n\(.*\)$/, "\1"/;tl'` (2认同)

phk*_*phk 10

使用awk

awk 'BEGIN { ORS="" } { print p"'"'"'"$0"'"'"'"; p=", " } END { print "\n" }' /path/to/list
Run Code Online (Sandbox Code Playgroud) 外壳转义较少的替代方案,因此更具可读性:
awk 'BEGIN { ORS="" } { print p"\047"$0"\047"; p=", " } END { print "\n" }' /path/to/list
Run Code Online (Sandbox Code Playgroud) 输出:
'd3heatmap', 'data.table', 'ggplot2', 'htmltools', 'htmlwidgets', 'metricsgraphics', 'networkD3', 'plotly', 'reshape2', 'scales', 'stringr'
Run Code Online (Sandbox Code Playgroud) 解释:

awk没有所有转义的脚本本身是BEGIN { ORS="" } { print p"'"$0"'"; p=", " } END { print "\n" }. 打印第一个条目后,变量p被设置(在此之前它就像一个空字符串)。使用此变量,p每个条目(或 in 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
Run Code Online (Sandbox Code Playgroud)

(假设您\r的文件中没有任何用于s 的用途。这里是非常安全的假设。)

或者,只需运行dos2unix /path/to/input.list一次即可就地转换文件。

  • `print p"'"'"'"$0"'"'"'"; p=", "`——神圣的引号,蝙蝠侠! (2认同)

jth*_*ill 7

正如@don_crissti 的链接答案所示,粘贴选项的速度非常快——Linux内核的管道比我想象的更高效,如果我不是刚刚尝试过它。值得注意的是,如果您对使用单个逗号分隔列表项而不是逗号 + 空格感到满意,那么粘贴管道

(paste -d\' /dev/null - /dev/null | paste -sd, -) <input
Run Code Online (Sandbox Code Playgroud)

甚至比合理的flex程序还要快(!)

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

但是,如果只是体面的性能是可以接受的(如果您没有进行压力测试,您将无法测量任何常数因子差异,它们都是即时的)并且您希望分隔符具有灵活性和合理性- 线性度,

sed "s/.*/'&'/;H;1h;"'$!d;x;s/\n/, /g'
Run Code Online (Sandbox Code Playgroud)

是你的票。是的,它看起来像线路噪音,但这个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); }
Run Code Online (Sandbox Code Playgroud)

在压力下,它比粘贴管道快 2-3 倍,粘贴管道本身至少比其他任何东西快 5 倍。