แยกส่วนของสตริงโดยใช้ bash / cut / split


121

ฉันมีสตริงดังนี้:

/var/cpanel/users/joebloggs:DNS9=domain.com

ฉันต้องการแยกชื่อผู้ใช้ ( joebloggs) จากสตริงนี้และเก็บไว้ในตัวแปร

รูปแบบของสตริงจะเป็นเช่นเดียวกันกับการยกเว้นjoebloggsและdomain.comดังนั้นฉันคิดสตริงสามารถแบ่งออกเป็นครั้งที่สองโดยใช้cut?

การแบ่งส่วนแรกจะแบ่งออก:และเราจะเก็บส่วนแรกไว้ในตัวแปรเพื่อส่งต่อไปยังฟังก์ชันการแบ่งตัวที่สอง

การแยกครั้งที่สองจะแบ่ง/และเก็บคำสุดท้าย ( joebloggs) ไว้ในตัวแปร

ฉันรู้วิธีทำใน php โดยใช้อาร์เรย์และการแยก แต่ฉันหลงทางนิดหน่อย

คำตอบ:


333

ในการแยกjoebloggsจากสตริงนี้ใน bash โดยใช้การขยายพารามิเตอร์โดยไม่มีกระบวนการเพิ่มเติมใด ๆ ...

MYVAR="/var/cpanel/users/joebloggs:DNS9=domain.com" 

NAME=${MYVAR%:*}  # retain the part before the colon
NAME=${NAME##*/}  # retain the part after the last slash
echo $NAME

ไม่ได้ขึ้นอยู่กับjoebloggsความลึกที่เฉพาะเจาะจงในเส้นทาง


สรุป

ภาพรวมของโหมดการขยายพารามิเตอร์บางตัวสำหรับการอ้างอิง ...

${MYVAR#pattern}     # delete shortest match of pattern from the beginning
${MYVAR##pattern}    # delete longest match of pattern from the beginning
${MYVAR%pattern}     # delete shortest match of pattern from the end
${MYVAR%%pattern}    # delete longest match of pattern from the end

ดังนั้น#หมายถึงการจับคู่ตั้งแต่เริ่มต้น (นึกถึงบรรทัดความคิดเห็น) และ%หมายถึงจากจุดสิ้นสุด อินสแตนซ์หนึ่งหมายถึงสั้นที่สุดและสองอินสแตนซ์หมายถึงยาวที่สุด

คุณสามารถรับสตริงย่อยตามตำแหน่งโดยใช้ตัวเลข:

${MYVAR:3}   # Remove the first three chars (leaving 4..end)
${MYVAR::3}  # Return the first three characters
${MYVAR:3:5} # The next five characters after removing the first 3 (chars 4-9)

คุณยังสามารถแทนที่สตริงหรือรูปแบบเฉพาะโดยใช้:

${MYVAR/search/replace}

patternอยู่ในรูปแบบเดียวกับชื่อไฟล์ที่ตรงกันดังนั้น*(ตัวอักษรใด ๆ ) เป็นเรื่องธรรมดาที่มักจะตามมาด้วยสัญลักษณ์เฉพาะเช่น/หรือ.

ตัวอย่าง:

กำหนดตัวแปรเช่น

MYVAR="users/joebloggs/domain.com" 

ลบพา ธ ออกจากชื่อไฟล์ (อักขระทั้งหมดไม่เกินเครื่องหมายทับ):

echo ${MYVAR##*/}
domain.com

ลบชื่อไฟล์ออกจากเส้นทาง (ลบการจับคู่ที่สั้นที่สุดหลังสุดท้าย/):

echo ${MYVAR%/*}
users/joebloggs

รับเฉพาะนามสกุลไฟล์ (ลบทั้งหมดก่อนช่วงเวลาสุดท้าย):

echo ${MYVAR##*.}
com

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

NAME=${MYVAR##*/}      # remove part before last slash
echo ${NAME%.*}        # from the new var remove the part after the last period
domain

ฉันไม่แน่ใจว่านี่เป็นข้อโต้แย้งสำหรับหรือต่อต้านการใช้ grep อย่างสร้างสรรค์ แต่ลองใช้ VAR = / here / is / a / path: with / a / colon / inside: DNS9 = domain.com
rici

2
หวาน! และทำได้ภายในเชลล์เรียกใช้งานจึงเร็วกว่าคำสั่งอื่น ๆ
stolsvik

3
@ ฟาดี้คุณต้องเปลี่ยนสัญลักษณ์แทนให้มาอยู่ข้างหน้าโคลอนและใช้#แทน%. ถ้าคุณต้องการเฉพาะส่วนที่อยู่หลังลำไส้ใหญ่สุดท้ายให้ใช้${MYVAR##*:}เพื่อรับส่วนหลังลำไส้ใหญ่แรกให้ใช้${MYVAR#*:}
beroe

4
เพื่อนคุณไม่รู้ว่ากี่ครั้งแล้วที่ฉันกลับมาหาคำตอบนี้ ขอบคุณ!
Joel B

1
ตอบโจทย์มาก! คำถาม: ถ้ารูปแบบของฉันเป็นตัวแปรฉันจะพิมพ์แบบนี้${RET##*$CHOP}หรือแบบนี้${RET##*CHOP}(หรือวิธีอื่น)? แก้ไข: ดูเหมือนจะเป็นอดีต${RET##*$CHOP}
Ctrl S

43

กำหนดฟังก์ชันดังนี้:

getUserName() {
    echo $1 | cut -d : -f 1 | xargs basename
}

และส่งสตริงเป็นพารามิเตอร์:

userName=$(getUserName "/var/cpanel/users/joebloggs:DNS9=domain.com")
echo $userName

1
คำตอบนี้ช่วยให้ฉันบรรลุสิ่งที่ฉันมาที่นี่ ไม่มีคำตอบที่ยอมรับและคำตอบนี้ได้รับการโหวตของฉันสำหรับความเรียบง่าย
harperville

1
การแก้ไขเท่านั้นที่ผมต้องทำในคำสั่งดังกล่าวถูกลบ ':' echo $1 | cut -d -f 1 | xargsเช่นนี้ +1 สำหรับ ans ที่เรียบง่ายและเรียบร้อย
Bhushan

20

สิ่งที่เกี่ยวกับ sed? ที่จะทำงานในคำสั่งเดียว:

sed 's#.*/\([^:]*\).*#\1#' <<<$string
  • #มีการใช้วงเวียน regex แทน/ตั้งแต่สตริงมี/อยู่ในนั้น
  • .*/ จับสตริงถึงแบ็กสแลชสุดท้าย
  • \( .. \)ทำเครื่องหมายกลุ่มการจับภาพ นี่คือ\([^:]*\).
    • [^:]กล่าวว่าตัวอักษรใด ๆ _except ลำไส้ใหญ่และ*วิธีการเป็นศูนย์หรือมากกว่า
  • .* หมายถึงส่วนที่เหลือของบรรทัด
  • \1หมายถึงแทนที่สิ่งที่พบในกลุ่มการดักจับแรก (และเท่านั้น) นี่คือชื่อ

นี่คือรายละเอียดที่ตรงกับสตริงกับนิพจน์ทั่วไป:

        /var/cpanel/users/           joebloggs  :DNS9=domain.com joebloggs
sed 's#.*/                          \([^:]*\)   .*              #\1       #'

ชำแหละสุดยอด!
kyb


10

ใช้ Awk เดียว:

... | awk -F '[/:]' '{print $5}'

นั่นคือใช้เป็นตัวคั่นฟิลด์/หรือ:ชื่อผู้ใช้จะอยู่ในฟิลด์ 5 เสมอ

หากต้องการเก็บไว้ในตัวแปร:

username=$(... | awk -F '[/:]' '{print $5}')

การใช้งานที่ยืดหยุ่นมากขึ้นโดยsedไม่จำเป็นต้องใช้ชื่อผู้ใช้ในฟิลด์ 5:

... | sed -e s/:.*// -e s?.*/??

นั่นคือทุกสิ่งที่ลบออกจากและเกินและแล้วทุกอย่างลบขึ้นจนสุดท้าย: อาจเร็วกว่าด้วยดังนั้นทางเลือกนี้จึงดีกว่าแน่นอน/sedawk

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