เปลี่ยนบรรทัดแยกเป็นรายการคั่นด้วยเครื่องหมายจุลภาคด้วยรายการที่ยกมา


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'

ขณะนี้ฉันมีไพพ์ไลน์ที่ไปจากไฟล์ raw ไปยังรายการด้านบน:

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

ฉันต้องการเพิ่มขั้นตอนเพื่อเปลี่ยนบรรทัดใหม่ให้เป็นรายการที่คั่นด้วยเครื่องหมายจุลภาค ฉันพยายามเพิ่มtr '\n' '","'ซึ่งล้มเหลว ฉันได้ลองคำตอบของ Stack Overflow ต่อไปนี้ด้วยซึ่งก็ล้มเหลวเช่นกัน

สิ่งนี้ก่อให้เกิดlibrary(stringr)))phics)ผลลัพธ์

สิ่งนี้ก่อให้เกิด,%ผลลัพธ์

คำตอบนี้ (เมื่อ-iนำแฟล็กออก) สร้างเอาต์พุตเหมือนกับอินพุต


ตัวคั่นต้องเป็นเครื่องหมายจุลภาคหรือไม่หรือเป็นเครื่องหมายจุลภาคเพียงอย่างเดียว
ขับเหล็ก

ไม่ว่าจะเป็นได้ แต่ฉันจะต้องเป็นตัวละครที่อ้างรอบสตริงอย่างใดอย่างหนึ่งหรือ' "
fbt


ฉันเป็นคนแรกที่สังเกตเห็นว่าข้อมูลที่ป้อนเข้าและสคริปต์ที่ใช้ในการประมวลผลนั้นเข้ากันไม่ได้อย่างสมบูรณ์ จะไม่มีเอาต์พุต
ctrl-alt-delor

สคริปต์ที่ฉันระบุไว้คือวิธีที่ฉันสร้างข้อมูลอินพุต มีคนถามหามัน ป้อนข้อมูลที่เกิดขึ้นจริงจะมีลักษณะบางอย่างเช่นนี้ โปรดทราบว่า Github เปลี่ยนการจัดรูปแบบเพื่อลบบรรทัดใหม่
fbt

คำตอบ:


19

คุณสามารถเพิ่มเครื่องหมายคำพูดด้วยsedแล้วรวมบรรทัดด้วยpasteเช่นนั้น:

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

True วาง "coreutils" รุ่นที่จะยอมรับทั้งสองรูปแบบ แต่ "-" เป็น POSIX มากกว่า ขอบคุณ !
Zeppelin

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ทุกรายการ (หรือในawk-speak: บันทึก ) จะถูกนำหน้าและพิมพ์เพิ่มเติมด้วยเครื่องหมายคำพูดเดี่ยวรอบ ๆ 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ไฟล์ใด ๆในไฟล์ของคุณสมมติฐานที่ปลอดภัยมากที่นี่)

หรือเรียกใช้เพียงdos2unix /path/to/input.listครั้งเดียวเพื่อแปลงไฟล์แบบแทนที่


เมื่อฉันเรียกใช้คำสั่งนี้ฉันได้รับ', 'stringr23aphicsเป็นผลลัพธ์
fbt

@fbt ดูบันทึกล่าสุดของฉัน
phk

2
print p"'"'"'"$0"'"'"'"; p=", "- คำพูดไร้สาระแบทแมน!
wchargin

ฉันรู้ว่าright‽ :) ฉันคิดเกี่ยวกับการกล่าวขวัญว่าในเปลือกหอยหลายพิมพ์p"'\''"$0"'\''";จะได้ยังทำงาน (ยังไม่ POSIXy แม้ว่า) หรือหรือใช้bash's C สตริง quoting ( $'') แม้เพียงprint p"\'"$0"\'";(อาจจำเป็นต้องมีการเสแสร้งเครื่องหมายอื่น ๆ แม้ว่า) แต่มี วิธีอื่นแล้วโดยใช้awkอักขระของ escapes
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-Y-Ness,

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

คือตั๋วของคุณ ใช่มันดูเหมือนว่าเส้นเสียง แต่H;1h;$!d;xสำนวนเป็นวิธีที่เหมาะสมในการ slurp ขึ้นทุกครั้งที่คุณสามารถรับรู้ว่าสิ่งที่ทั้งได้รับจริงง่ายต่อการอ่านก็s/.*/'&'/ตามด้วย Slurp 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 เท่า


1
(paste -d\ \'\' /dev/null /dev/null - /dev/null | paste -sd, -) <infile | cut -c2-จะทำเครื่องหมายจุลภาค + space @ ค่อนข้างเร็วเหมือนที่คุณสังเกตไว้มันไม่ยืดหยุ่นจริง ๆ ถ้าคุณต้องการสตริงแฟนซีเป็นตัวคั่น
don_crissti

ว่าflexสิ่งที่เป็นคนเย็นแช่งสวย ... นี่เป็นครั้งแรกที่ฉันเห็นโพสต์คนflexโค้ดบนเว็บไซต์นี้ ... upvote ใหญ่! โปรดโพสต์สิ่งนี้เพิ่มเติม
don_crissti

@don_crissti ขอบคุณ! ฉันจะมองหาโอกาสที่ดี sed / awk / whatnot มักจะเป็นตัวเลือกที่ดีกว่าเพียงเพื่อความสะดวกสบาย แต่มักจะมีคำตอบที่ยืดหยุ่นง่ายเช่นกัน
jthill

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 loop เข้าสู่รายการ
iruvar

@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 ทั้งหมดเป็นเชลล์ที่ใช้มากที่สุด) มันก็ยังไม่ควรได้รับอนุญาต นอกจากนี้ยังมีชิ้นส่วนที่คุณสามารถทำได้ดีกว่าในการอ้างอิง (ใส่เครื่องหมายคำพูดคู่) ตัวอย่างเช่นในขณะที่ชื่อแพ็กเกจไม่น่าจะมีช่องว่างอยู่ในนั้นมันก็ยังเป็นแบบแผนที่ดีในการอ้างอิงตัวแปรแทนที่จะไม่ใช่คุณอาจต้องการรัน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( "ทีละบรรทัด") ในบรรทัดคำสั่งที่รอสำหรับการป้อนข้อมูล, วางเนื้อหาจากคลิปบอร์ดกดและผลตอบแทนที่ฟังก์ชั่นการป้อนข้อมูลที่ล้อมรอบไปด้วย<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 '...'

--Paul


2
ยินดีต้อนรับสู่ Unix.stackexchange! ผมขอแนะนำให้คุณใช้เวลาทัวร์
สตีเฟ่น Rauch

0

เป็นเรื่องตลกที่จะใช้รายการข้อความธรรมดาของแพ็คเกจ R เพื่อติดตั้งใน R ไม่มีใครเสนอวิธีแก้ปัญหาโดยใช้รายการนั้นโดยตรงใน R แต่ต่อสู้กับ bash, perl, python, awk, awk, sed หรืออะไรก็ตามที่ใส่เครื่องหมายคำพูดและเครื่องหมายจุลภาคใน รายการ. สิ่งนี้ไม่จำเป็นเลยและยิ่งไปกว่านั้นไม่ได้แก้ปัญหาวิธีการอินพุตและใช้รายการที่แปลงใน R

คุณก็สามารถโหลดไฟล์ข้อความธรรมดา (กล่าวว่าpackages.txt) เป็น dataframe 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.