วิธีการแยกสตริงในเชลล์และรับฟิลด์สุดท้าย


293

สมมติว่าฉันมีสตริง1:2:3:4:5และฉันต้องการได้รับเขตข้อมูลสุดท้าย ( 5ในกรณีนี้) ฉันจะทำเช่นนั้นโดยใช้ Bash ได้อย่างไร ฉันพยายามแต่ผมไม่ทราบว่าวิธีการระบุข้อมูลที่ผ่านมาด้วยcut-f

คำตอบ:


430

คุณสามารถใช้ตัวดำเนินการสตริง :

$ foo=1:2:3:4:5
$ echo ${foo##*:}
5

สิ่งนี้จดจ้องทุกอย่างจากด้านหน้าจนถึง ':' อย่างตะกละตะกลาม

${foo  <-- from variable foo
  ##   <-- greedy front trim
  *    <-- matches anything
  :    <-- until the last ':'
 }

7
ในขณะที่สิ่งนี้ทำงานได้สำหรับปัญหาที่กำหนดคำตอบของ William ด้านล่าง ( stackoverflow.com/a/3163857/520162 ) ก็จะส่งกลับ5เช่นกันหากสตริงคือ1:2:3:4:5:(ในขณะที่ใช้ตัวดำเนินการสตริงจะให้ผลลัพธ์ที่ว่างเปล่า) สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อแยกวิเคราะห์เส้นทางที่อาจมี (หรือไม่) /อักขระสิ้นสุด
eckes

9
แล้วคุณจะทำสิ่งตรงกันข้ามนี้ได้อย่างไร? เพื่อสะท้อน '1: 2: 3: 4:'?
Dobz

12
และจะเก็บส่วนหนึ่งไว้ก่อนตัวแยกสุดท้ายได้อย่างไร ${foo%:*}เห็นได้ชัดโดยใช้ #- ตั้งแต่ต้น %- จากจุดสิ้นสุด #, %- การแข่งขันที่สั้น; ##, %%- การแข่งขันที่ยาวที่สุด
หมดเวลา Danila

1
หากฉันต้องการได้รับองค์ประกอบสุดท้ายจากเส้นทางฉันจะใช้มันได้อย่างไร echo ${pwd##*/}ไม่ทำงาน, ไม่เป็นผล.
Putnik

2
@Putnik คำสั่งนั้นเห็นpwdว่าเป็นตัวแปร ลองdir=$(pwd); echo ${dir##*/}ดู ใช้งานได้สำหรับฉัน!
Stan Strum

344

อีกวิธีหนึ่งคือการย้อนกลับก่อนและหลังcut:

$ echo ab:cd:ef | rev | cut -d: -f1 | rev
ef

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


17
คำตอบนี้ดีเพราะใช้ 'ตัด' ซึ่งผู้เขียนคุ้นเคย (แล้ว) นอกจากนี้ฉันชอบคำตอบนี้เพราะฉันใช้ 'ตัด' และมีคำถามที่แน่นอนนี้ดังนั้นการค้นหาหัวข้อนี้ผ่านการค้นหา
Dannid

6
อาหารสัตว์ที่ตัดแล้ววางสำหรับคนที่ใช้ช่องว่างเป็นตัวคั่น:echo "1 2 3 4" | rev | cut -d " " -f1 | rev
funroll

2
rev | ตัด -d -f1 | rev เป็นคนฉลาดมาก! ขอบคุณ! ช่วยฉันได้พวง (กรณีการใช้งานของฉันคือ rev | -d '' -f 2- | rev
EdgeCaseBerg

1
ฉันมักจะลืมrevมันเป็นสิ่งที่ฉันต้องการ! cut -b20- | rev | cut -b10- | rev
shearn89

ฉันลงเอยด้วยวิธีนี้ความพยายามของฉันในการตัดพา ธ ของไฟล์ด้วย "awk -F" / "'{print $ NF}'" แตกค่อนข้างสำหรับฉันเนื่องจากชื่อไฟล์รวมถึงพื้นที่สีขาวก็ถูกตัดออกด้วย
THX

76

เป็นเรื่องยากที่จะทำให้ฟิลด์สุดท้ายใช้การตัด แต่นี่คือวิธีแก้ปัญหาบางอย่างใน awk และ perl

echo 1:2:3:4:5 | awk -F: '{print $NF}'
echo 1:2:3:4:5 | perl -F: -wane 'print $F[-1]'

5
ประโยชน์ที่ดีของการแก้ปัญหานี้มากกว่าคำตอบที่ได้รับการยอมรับ: มันก็ตรงกับเส้นทางที่ประกอบด้วยหรือไม่ได้มีการตกแต่ง/ตัวอักษร: /a/b/c/dและ/a/b/c/d/เกิดผลเหมือนกัน ( d) pwd | awk -F/ '{print $NF}'เมื่อการประมวลผล คำตอบที่ได้รับการยอมรับส่งผลให้เกิดผลลัพธ์ที่ว่างเปล่าในกรณีของ/a/b/c/d/
eckes

@eckes ในกรณีของโซลูชัน AWK บน GNU bash เวอร์ชัน 4.3.48 (1) - ปล่อยนั้นไม่เป็นความจริงเพราะมันสำคัญเมื่อใดก็ตามที่คุณมีเครื่องหมายทับหรือไม่ เพียงแค่ใส่ AWK จะใช้/เป็นตัวคั่นและถ้าเส้นทางของคุณ/my/path/dir/มันจะใช้ค่าหลังจากตัวคั่นล่าสุดซึ่งเป็นเพียงสตริงที่ว่างเปล่า ดังนั้นจึงเป็นการดีที่สุดที่จะหลีกเลี่ยงการต่อท้ายสแลชหากคุณต้องการทำสิ่งเช่นฉัน
ต๊อ

ฉันจะได้รับสตริงย่อยจนถึงฟิลด์สุดท้ายได้อย่างไร
blackjacx

1
@blackjacx มีนิสัยใจคอบางอย่าง แต่สิ่งที่ชอบawk '{$NF=""; print $0}' FS=: OFS=:มักจะทำงานได้ดีพอ
William Pursell

29

สมมติว่าการใช้งานค่อนข้างง่าย (ตัวอย่างเช่นไม่ใช้ตัวคั่นใด ๆ ) คุณสามารถใช้ grep:

$ echo "1:2:3:4:5" | grep -oE "[^:]+$"
5

รายละเอียด - ค้นหาอักขระทั้งหมดที่ไม่ใช่ตัวคั่น ([^:]) ที่ท้ายบรรทัด ($) - พิมพ์เฉพาะส่วนที่จับคู่เท่านั้น


- E หมายถึงการใช้ไวยากรณ์เพิ่มเติม; [^ ... ] หมายถึงอะไรนอกจากตัวอักษรที่อยู่ในรายการ; + อย่างน้อยหนึ่งครั้งการเข้าชม (จะใช้ความยาวสูงสุดที่เป็นไปได้สำหรับรูปแบบรายการนี้เป็นส่วนขยาย gnu) - ตัวอย่างเช่นตัวคั่นการแยกเป็นเครื่องหมายโคลอน
Alexander Stohr

18

ทางเดียว:

var1="1:2:3:4:5"
var2=${var1##*:}

อื่นโดยใช้อาร์เรย์:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
var2=${var2[@]: -1}

อีกอันหนึ่งกับอาเรย์:

var1="1:2:3:4:5"
saveIFS=$IFS
IFS=":"
var2=($var1)
IFS=$saveIFS
count=${#var2[@]}
var2=${var2[$count-1]}

การใช้ Bash (รุ่น> = 3.2) การแสดงออกปกติ:

var1="1:2:3:4:5"
[[ $var1 =~ :([^:]*)$ ]]
var2=${BASH_REMATCH[1]}

11
$ echo "a b c d e" | tr ' ' '\n' | tail -1
e

tail -1เพียงแค่ตัวคั่นแปลลงในการขึ้นบรรทัดใหม่และเลือกรายการสุดท้ายด้วย


มันจะล้มเหลวหากรายการสุดท้ายมี\nแต่สำหรับกรณีส่วนใหญ่เป็นวิธีการแก้ปัญหาที่อ่านได้มากที่สุด
Yajo

7

การใช้sed:

$ echo '1:2:3:4:5' | sed 's/.*://' # => 5

$ echo '' | sed 's/.*://' # => (empty)

$ echo ':' | sed 's/.*://' # => (empty)
$ echo ':b' | sed 's/.*://' # => b
$ echo '::c' | sed 's/.*://' # => c

$ echo 'a' | sed 's/.*://' # => a
$ echo 'a:' | sed 's/.*://' # => (empty)
$ echo 'a:b' | sed 's/.*://' # => b
$ echo 'a::c' | sed 's/.*://' # => c

4

หากฟิลด์สุดท้ายของคุณเป็นอักขระตัวเดียวคุณสามารถทำสิ่งนี้ได้:

a="1:2:3:4:5"

echo ${a: -1}
echo ${a:(-1)}

ตรวจสอบการจัดการสตริงในทุบตี


นี้ไม่ได้ทำงาน: มันจะช่วยให้สุดท้ายตัวละครของaไม่สุดท้ายฟิลด์
gniourf_gniourf

1
จริงนั่นคือความคิดถ้าคุณรู้ว่าความยาวของสนามสุดท้ายนั้นดีแค่ไหน ถ้าไม่ได้คุณต้องใช้อย่างอื่น ...
Ab Irato

4

มีคำตอบที่ดีมากมายที่นี่ แต่ฉันยังต้องการแบ่งปันโดยใช้basename :

 basename $(echo "a:b:c:d:e" | tr ':' '/')

อย่างไรก็ตามมันจะล้มเหลวถ้ามี '/' อยู่ในสตริงของคุณอยู่แล้วอยู่แล้ว หาก slash / เป็นตัวคั่นของคุณคุณเพียงแค่ต้อง (และควร) ใช้ชื่อฐาน

มันไม่ใช่คำตอบที่ดีที่สุด แต่มันแสดงให้เห็นว่าคุณสร้างสรรค์ได้อย่างไรโดยใช้คำสั่ง bash



2
echo "a:b:c:d:e"|xargs -d : -n1|tail -1

การใช้งานครั้งแรก xargs แยกโดยใช้ ":", - n1 หมายถึงทุกบรรทัดมีเพียงส่วนเดียวจากนั้น pring ส่วนสุดท้าย


1
for x in `echo $str | tr ";" "\n"`; do echo $x; done

2
ปัญหานี้จะเกิดขึ้นหากมีพื้นที่ว่างในฟิลด์ใด ๆ นอกจากนี้มันไม่ได้ตอบคำถามการดึงข้อมูลในฟิลด์สุดท้ายโดยตรง
chepner

1

สำหรับผู้ที่พอใจกับ Python https://github.com/Russell91/pythonpyเป็นทางเลือกที่ดีในการแก้ปัญหานี้

$ echo "a:b:c:d:e" | py -x 'x.split(":")[-1]'

จากความช่วยเหลือ pythonpy -x treat each row of stdin as xนี้:

ด้วยเครื่องมือนั้นมันเป็นเรื่องง่ายที่จะเขียนโค้ดไพ ธ อนที่นำไปใช้กับอินพุต


1

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

[chris@desktop bin]$ echo 1:2:3:4:5 | rev | cut -d: -f1
5

มันเป็นสิ่งสำคัญที่จะต้องทราบว่าถ้าใช้วิธีนี้และตัวเลขที่มีขนาดใหญ่กว่า 1 หลัก (หรือมากกว่าหนึ่งตัวอักษรในทุกสถานการณ์) คุณจะต้องเรียกใช้คำสั่ง 'rev' อีกครั้งผ่านเอาต์พุตแบบไพพ์

[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1
42
[chris@desktop bin]$ echo 1:2:3:4:5:8:24 | rev | cut -d: -f1 | rev
24

หวังว่าฉันสามารถช่วยได้ไชโย


1

วิธีการแก้ปัญหาโดยใช้ read builtin:

IFS=':' read -a fields <<< "1:2:3:4:5"
echo "${fields[4]}"

หรือเพื่อให้ทั่วไปมากขึ้น:

echo "${fields[-1]}" # prints the last item

0

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

# install pythonp
pythonp -m pip install pythonp

echo "1:2:3:4:5" | pythonp "l.split(':')[-1]"
5

หลามสามารถทำได้โดยตรง: echo "1:2:3:4:5" | python -c "import sys; print(list(sys.stdin)[0].split(':')[-1])"
MortenB

@MortenB คุณเข้าใจผิด จุดประสงค์ทั้งหมดของpythonpแพ็คเกจคือการทำให้คุณทำสิ่งเดียวpython -cกับที่มีการพิมพ์ตัวอักษรน้อยลง โปรดดู README ในที่เก็บ
ระเบิด

0

การจับคู่ Regex sedเป็นโลภมาก (มักจะเกิดขึ้นครั้งล่าสุด) ซึ่งคุณสามารถใช้เพื่อประโยชน์ของคุณที่นี่:

$ foo=1:2:3:4:5
$ echo ${foo} | sed "s/.*://"
5
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.