bash: วิธีที่สั้นที่สุดในการรับคอลัมน์ที่ n ของผลลัพธ์


166

สมมติว่าในระหว่างวันทำงานของคุณคุณจะพบรูปแบบต่อไปนี้ของเอาต์พุตคอลัมน์จากคำสั่งบางคำใน bash (ในกรณีของฉันจากการดำเนินการsvn stในไดเรกทอรีทำงาน Rails ของฉัน):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

เพื่อทำงานกับเอาต์พุตของคำสั่งของคุณ - ในกรณีนี้ชื่อไฟล์ - จำเป็นต้องมีการแยกวิเคราะห์บางอย่างเพื่อให้คอลัมน์ที่สองสามารถใช้เป็นอินพุตสำหรับคำสั่งถัดไป

สิ่งที่ฉันได้ทำคือการใช้awkเพื่อรับที่คอลัมน์ที่สองเช่นเมื่อฉันต้องการลบไฟล์ทั้งหมด (ไม่ใช่ว่าเป็น usecase ทั่วไป :) ฉันจะทำ:

svn st | awk '{print $2}' | xargs rm

เนื่องจากฉันพิมพ์สิ่งนี้บ่อยๆคำถามธรรมชาติคือ: มีวิธีที่สั้นกว่า

หมายเหตุ: สิ่งที่ฉันถามเป็นหลักเป็นคำถามคำสั่งเชลล์แม้ว่าตัวอย่างที่เป็นรูปธรรมของฉันอยู่ในเวิร์กโฟลว์ svn ของฉัน หากคุณรู้สึกว่าเวิร์กโฟลว์นั้นโง่และแนะนำวิธีการอื่นฉันอาจไม่ลงคะแนนให้คุณ แต่คนอื่น ๆ อาจเนื่องจากคำถามที่นี่เป็นวิธีการรับเอาต์พุตคอลัมน์คำสั่ง n-th ใน bash อย่างสั้นที่สุดเท่าที่จะทำได้ . ขอบคุณ :)


1
เมื่อคุณใช้คำสั่งบ่อยครั้งคุณควรสร้างสคริปต์และวางไว้ในพา ธ ของคุณ คุณสามารถสร้างฟังก์ชั่นใน bashrc ของคุณถ้าคุณชอบ ฉันไม่เห็นจุดลดนิพจน์การเลือกคอลัมน์
ลินช์

1
คุณพูดถูกและฉันอาจทำอย่างนั้น 'การจุดคือการแสวงหาวิธีการใหม่ที่จะทำสิ่งที่อยู่ในทุบตีสำหรับวัตถุประสงค์ของการเรียนรู้ แต่ส่วนใหญ่เพื่อความสนุกสนาน :)
SV1

1
นอกจากนี้คุณไม่มี. bashrc ของคุณเมื่อ ssh-ing อยู่ที่ไหนสักแห่งดังนั้นจึงเป็นประโยชน์ที่จะรู้วิธีของคุณโดยไม่ได้
Kos

คำตอบ:


125

คุณสามารถใช้cutเพื่อเข้าถึงฟิลด์ที่สอง:

cut -f2

แก้ไข: ขออภัยไม่ทราบว่า SVN ไม่ได้ใช้แท็บในเอาต์พุตดังนั้นจึงไม่มีประโยชน์เลย คุณสามารถปรับแต่งcutให้ได้ผลลัพธ์ แต่มันค่อนข้างบอบบาง - บางอย่างเช่นcut -c 10- จะใช้งานได้ แต่ค่าที่แน่นอนจะขึ้นอยู่กับการตั้งค่าของคุณ

อีกทางเลือกหนึ่งคือ: sed 's/.\s\+//'


1
ลองใช้cut -f2กับsvn stผลลัพธ์ คุณจะเห็นว่ามันใช้งานไม่ได้
ลินช์

ฉันคิดว่าของจริงsvn stจะใช้แท็บในผลลัพธ์ หลังจากการทดสอบบางอย่างดูเหมือนว่านี่ไม่ใช่กรณี ประณาม.
porges

21
ผ่านตัวคั่นที่เหมาะสมเพื่อ-dให้สิ่งนี้ทำงานได้
โยเกิร์ต

6
เพื่อขยายในสิ่งที่ @Yogh cut -d" " -f2กล่าวว่าสำหรับพื้นที่เป็นตัวคั่นก็จะมีลักษณะเหมือน
adamyonk

W / o awk บน stdout ใช้ xargs: svn st | xargs | cut -d "" -f2
vr286

106

หากต้องการบรรลุสิ่งเดียวกันกับ:

svn st | awk '{print $2}' | xargs rm

ใช้ bash เท่านั้นคุณสามารถใช้:

svn st | while read a b; do rm "$b"; done

ได้รับมันไม่สั้น แต่มีประสิทธิภาพมากกว่าและจัดการกับช่องว่างในชื่อไฟล์ของคุณได้อย่างถูกต้อง


คืออะไรaและbพวกเขาได้อย่างไร
Timo

1
@Timo aหมายถึงคอลัมน์แรกและbแสดงถึงคอลัมน์ที่เหลือ หากคุณต้องการที่จะพิมพ์คอลัมน์ที่สองใช้read a b c;แล้วใช้แทนecho rmฉันใช้สิ่งนี้เพื่อรับกระบวนการ ID ที่เป็นไปตามรูปแบบ grep ดังนั้นฉันจึงสามารถขัดจังหวะพวกเขาทั้งหมด
Matt Kleinsmith

37

ฉันพบว่าตัวเองอยู่ในสถานการณ์เดียวกันและจบลงด้วยการเพิ่มชื่อแทนเหล่านี้ลงใน.profileไฟล์ของฉัน:

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

ซึ่งทำให้ฉันสามารถเขียนสิ่งนี้:

svn st | c2 | xargs rm

15
ฟังก์ชั่นทุบตีมักจะมีประโยชน์มากกว่า ฉันทำสิ่งนี้: function c() { awk "{print \$$1}" } จากนั้นคุณสามารถทำได้: svn st | c 2 | xargs rm
Aissen

8
แต่ฉันต้องพิมพ์พื้นที่เพิ่มเติม เหนื่อยเกินไป :)
StackedCrooked

16

ลอง zsh รองรับนามแฝงต่อท้ายดังนั้นคุณสามารถกำหนด X ใน. zshrc ของคุณให้เป็น

alias -g X="| cut -d' ' -f2"

จากนั้นคุณสามารถทำได้:

cat file X

คุณสามารถก้าวไปอีกขั้นหนึ่งแล้วกำหนดมันสำหรับคอลัมน์ที่ n:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

ซึ่งจะส่งออกคอลัมน์ที่ n ของไฟล์ "file" คุณสามารถทำสิ่งนี้เพื่อเอาท์พุท grep หรือเอาท์พุทน้อยเกินไป มันมีประโยชน์มากและเป็นคุณสมบัติที่น่าสนใจของ zsh

คุณสามารถไปอีกขั้นหนึ่งและกำหนด D เป็น:

alias -g D="|xargs rm"

ตอนนี้คุณสามารถพิมพ์:

cat file X1 D

เพื่อลบไฟล์ทั้งหมดที่กล่าวถึงในคอลัมน์แรกของไฟล์ "ไฟล์"

ถ้าคุณรู้ว่า bash นั้น zsh นั้นไม่ได้มีการเปลี่ยนแปลงอะไรมากมายยกเว้นคุณสมบัติใหม่บางอย่าง

HTH คริส


อ่าฉันเห็นว่าคุณได้อัปเดตคำตอบของคุณในขณะที่ฉันกำลังพิมพ์ความคิดเห็นของฉันด้านบน :) คุณช่วยระบุคอลัมน์ที่จะรับได้หรือฉันต้องการ n-line ใน. zshrc ของฉัน (ไม่ใช่สิ่งที่สำคัญแค่อยากรู้อยากเห็น)
Sv1

ฉันแก้ไขโพสต์ของฉันเพิ่มเติมและกำหนดคำต่อท้าย "D" เพื่อลบไฟล์ เท่าที่ฉันรู้คุณจะต้องเพิ่มบรรทัดสำหรับแต่ละคำต่อท้าย
คริส

หรือทำให้มันเป็นพารามิเตอร์แรกของคำสั่ง X สิ่งนี้ไม่จำเป็นต้องใช้ zsh หรือ alias ยกเว้นอาจเป็นเพราะความคิดแปลก ๆ ที่จะนำไปป์ในนามแฝง
tripleee

1
ฉันไม่เข้าใจว่าทำไมพวกคุณถึงชอบcutตัวเลือก svn stมันก็ไม่ทำงานบนเครื่องของฉันโดยใช้การส่งออกของ ลองsvn st | cut -d' ' -f2ดูว่าเกิดอะไรขึ้น
ลินช์

@ Sv1 คุณสามารถเขียน loop เพื่อกำหนดนามแฝงเนื่องจาก alias เป็นเพียงคำสั่ง
Driftcatcher

8

เนื่องจากคุณไม่คุ้นเคยกับสคริปต์นี่เป็นตัวอย่าง

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

หากคุณบันทึกสิ่งนี้ไว้~/bin/xและตรวจสอบให้แน่ใจว่า~/binอยู่ในPATH(ตอนนี้นั่นคือสิ่งที่คุณสามารถทำได้และควรใส่ไว้ใน.bashrc) คุณจะมีคำสั่งสั้นที่สุดสำหรับการแยกคอลัมน์ n; x n

สคริปต์ควรทำการตรวจสอบข้อผิดพลาดและการประกันตัวที่เหมาะสมหากเรียกใช้ด้วยอาร์กิวเมนต์ที่ไม่ใช่ตัวเลขหรือจำนวนอาร์กิวเมนต์ที่ไม่ถูกต้อง ฯลฯ แต่การขยายในเวอร์ชั่นที่จำเป็นของกระดูกเปล่านี้จะอยู่ในบทที่ 102

บางทีคุณอาจต้องการขยายสคริปต์เพื่ออนุญาตตัวคั่นคอลัมน์อื่น Awk ตามค่าเริ่มต้นแยกวิเคราะห์อินพุตในฟิลด์บนช่องว่าง หากต้องการใช้ตัวคั่นอื่นให้ใช้-F ':'ตำแหน่งที่:เป็นตัวคั่นใหม่ การใช้สิ่งนี้เป็นตัวเลือกในสคริปต์ทำให้ใช้งานได้นานขึ้นเล็กน้อยดังนั้นฉันจึงทิ้งเรื่องนั้นไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน


การใช้

รับไฟล์file:

1 2 3
4 5 6

คุณสามารถผ่านมันผ่าน stdin (ใช้ไร้ประโยชน์catเพียงตัวยึดตำแหน่งสำหรับสิ่งที่มีประโยชน์มากกว่า);

$ cat file | sh script.sh 2
2
5

หรือระบุเป็นอาร์กิวเมนต์ของสคริปต์:

$ sh script.sh 2 file
2
5

ที่นี่sh script.shสมมุติว่าสคริปต์ถูกบันทึกเช่นเดียวกับscript.shในไดเรกทอรีปัจจุบัน หากคุณบันทึกด้วยชื่อที่มีประโยชน์มากกว่าในที่ของคุณPATHและทำเครื่องหมายว่าสามารถใช้งานได้ดังคำแนะนำข้างต้นให้ใช้ชื่อที่มีประโยชน์แทน (และไม่ใช่sh) อย่างชัดเจน


เป็นความคิดที่ดี แต่ไม่ได้ผลสำหรับฉันอย่างที่เป็นอยู่กับ sh หรือทุบตี ใช้งานได้: #! / bin / bash col = $ 1 shift awk "{print \ $$ col}"
mgk

ขอบคุณสำหรับความคิดเห็นที่ล่าช้านี้ อัปเดตพร้อมการแก้ไขของคุณ
tripleee


@ fedorqui ขอบคุณสำหรับความคิดเห็นของคุณที่นี่และที่อื่น ๆ ! อัปเดตคำตอบ ตอนนี้ใช้เวลานานขึ้นเล็กน้อยดังนั้นหากคุณต้องการสคริปต์สั้นที่สุดที่คุณมีให้ดูประวัติการแก้ไขสำหรับเวอร์ชั่นดั้งเดิม
tripleee

1
@ fedorqui ขอบคุณพวง! เพิ่มข้อแม้เล็ก ๆ ลงในcatตัวอย่างเพื่อเหตุผลที่เป็นโรค
ฮินดู

5

ดูเหมือนว่าคุณจะมีทางออกอยู่แล้ว เพื่อให้ง่ายขึ้นทำไมไม่เพียงแค่วางคำสั่งของคุณลงใน bash script (ด้วยชื่อย่อ) แล้วเรียกใช้มันแทนที่จะพิมพ์คำสั่ง 'ยาว' ทุกครั้ง?


มันเป็นเพียงว่ามันไม่ 'เย็น' ในความคิดของฉัน ทำไม? ดีฉันไม่ต้องการที่จะทำให้. bashrc ของฉันเต็มไปด้วยทางลัดต่าง ๆ ที่ทำสิ่งนี้และนั่นคือการเขียนเมตาเชลล์ มันสวยสมนามขึ้นมาเหมือนเดิม ต้องบอกว่าคำแนะนำของคุณไม่เลว
Sv1

2
.bashrc? ทำไมคุณถึงยุ่งกับสิ่งที่ไม่เกี่ยวข้องกับการทุบตี สิ่งที่ฉันทำคือฉันเขียนสคริปต์แต่ละคำสั่งสำหรับ 'ชุด' ของคำสั่งและวางไว้ใน~/scriptsไดเรกทอรี จากนั้นเพิ่มทั้งหมด~/scriptsลงในของฉันPATHดังนั้นฉันสามารถเรียกพวกเขาด้วยชื่อเมื่อฉันต้องการพวกเขา
Tarek Fadel

4
จากนั้นอย่าวางไว้ใน. bashrc ของคุณ สร้างสคริปต์ใน ~ / bin และตรวจสอบให้แน่ใจว่าอยู่ในเส้นทางของคุณ
tripleee

2

หากคุณตกลงกับการเลือกคอลัมน์ด้วยตนเองคุณอาจเลือกใช้อย่างรวดเร็ว:

svn st | pick | xargs rm

เพียงไปที่เซลล์ใดก็ได้ในคอลัมน์ที่ 2 กดcจากนั้นกดenter


ฉันลองใช้เครื่องมือ "เลือก" ที่คุณอ้างอิงและฉันชอบมันมาก เป็นเรื่องที่ดีมากสำหรับหลาย ๆ สถานการณ์ที่ฉันต้องการพิมพ์คอลัมน์ที่เลือก แต่ไม่ต้องการพิมพ์ "awk '{print $ 3}'" เพื่อรับคอลัมน์ที่ต้องการ น่าเสียดายที่ชื่อมีความขัดแย้งกับเครื่องมือ "เลือก" ที่แตกต่างกันซึ่งดูเหมือนว่าจะได้รับความนิยมมากกว่า - สามารถติดตั้งด้วย "apt install pick" ดังนั้นฉันจึงเปลี่ยนชื่อเครื่องมือของคุณเป็น "pickk" บนระบบของฉันและตั้งใจจะใช้ต่อไป ขอขอบคุณที่โพสต์ข้อมูลอ้างอิง
William Dye

1

หมายเหตุพา ธ ของไฟล์นั้นไม่จำเป็นต้องอยู่ในคอลัมน์ที่สองของเอาต์พุต svn st ตัวอย่างเช่นถ้าคุณแก้ไขไฟล์และแก้ไขคุณสมบัติมันจะเป็นคอลัมน์ที่ 3

ดูตัวอย่างผลลัพธ์ที่เป็นไปได้ใน:

svn help st

ตัวอย่างผลลัพธ์:

 M     wc/bar.c
A  +   wc/qax.c

ฉันแนะนำให้ตัด 8 ตัวอักษรแรกโดย:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

ถ้าคุณต้องการที่จะแน่ใจ 100% และจัดการกับชื่อไฟล์แฟนซีที่มีพื้นที่สีขาวในตอนท้ายคุณจะต้องแยกวิเคราะห์ xml output:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

แน่นอนคุณอาจต้องการใช้ตัวแยกวิเคราะห์ XML จริง ๆ แทน grep / sed

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.