ตัดตัวละครทั้งหมดหลังจาก /


14

ฉันจะตัดตัวละครทั้งหมดหลังจาก '/' สุดท้ายได้อย่างไร

ข้อความนี้

xxxx/x/xx/xx/xxxx/x/yyyyy
xxx/xxxx/xxxxx/yyy

ควรกลับมา

xxxx/x/xx/xx/xxxx/x
xxx/xxxx/xxxxx

เป็นคำถามที่ดีฉันเพิ่งรู้ว่าเราไม่เข้าใจคำถามในลักษณะเดียวกัน
Félicien

3
คำสั่งbasenameและdirnameทำสิ่งนี้แล้ว
Michael Hampton

ฉันไม่คิดว่าการแก้ไข # 5 สำหรับคำถามนั้นถูกต้อง การใช้คำว่า "ตัด" อาจไม่ชัดเจน ถ้าคุณตัดบางสิ่งคุณทิ้งส่วนนั้นไปหรือคุณเก็บไว้แค่นั้นหรือ? อย่างไรก็ตามถ้อยคำก่อนหน้านี้ "และเพื่อละเว้นสิ่งที่อยู่ก่อนหน้า /" นั้นไม่คลุมเครือ นี่เปลี่ยนไปเป็นสิ่งที่ตรงกันข้าม
egmont

@ คำว่า "ตัด" ตัวเองถูกนำมาจากต้นฉบับ: "ฉันต้องตัดเฉพาะตัวอักษรหลังจากที่ผ่านมา/" กลายเป็น "ฉันจะตัดตัวละครทั้งหมดหลังจาก '/' สุดท้ายได้อย่างไร? ซึ่งเป็นการเปลี่ยนแปลงของการใช้ถ้อยคำที่รักษาความหมายไว้อย่างสมเหตุสมผล IMO หากมีอะไรฉันรู้สึกว่า "ละเว้นอะไรคือ ... " ส่วนที่คลุมเครือ: ตัวอย่างดูเหมือนจะเก็บสิ่งที่อยู่/ข้างหน้า ตัวอย่างทำให้ชัดเจนแล้วล่ะค่ะ
muru

@muru ฉันกำลังพูดถึงการแก้ไขที่เพิ่มตัวอย่างไม่ใช่การแก้ไขของคุณ
egmont

คำตอบ:


15

หากคุณต้องการได้ "ส่วนที่ถูกตัด"

yyy
yyyyy

คุณสามารถใช้ได้

sed 's|.*/||' 

เช่น.

echo "xxx/xxxx/xxxxx/yyy" | sed 's|.*/||'
echo "xxxx/x/xx/xx/xxxx/x/yyyyy" | sed 's|.*\/||'

เอาท์พุต

yyy
yyyyy

(หมายเหตุ: สิ่งนี้ใช้ความสามารถของ sed ในการใช้ตัวคั่นนอกเหนือจาก / ในกรณีนี้ | ในคำสั่ง s)


หากคุณต้องการได้รับจุดเริ่มต้นของสตริง:

xxx/xxxx/xxxxx
xxxx/x/xx/xx/xxxx/x

คุณสามารถใช้ได้

sed 's|\(.*\)/.*|\1|'

เช่น.

echo "xxx/xxxx/xxxxx/yyy" | sed 's|\(.*\)/.*|\1|'
echo "xxxx/x/xx/xx/xxxx/x/yyyyy" | sed 's|\(.*\)/.*|\1|'

เอาท์พุต

xxx/xxxx/xxxxx
xxxx/x/xx/xx/xxxx/x

6
สวัสดีครับในกรณีนี้คุณสามารถเปลี่ยนคั่นเช่นแทน# /นอกจากนี้คุณสามารถใช้ตัวเลือก-rถ้าคุณไม่ต้องการที่จะหลบหนี `\` sed -r 's#(^.*)/.*$#\1#'แต่ละอักขระพิเศษ:
pa4080

1
ฉันได้ส่งการแก้ไขที่เสนอเพื่อใช้ | แทน /. หากคุณไม่ชอบฉันขอแนะนำให้คุณ "ปฏิเสธและแก้ไข" และเปลี่ยน "ขอทาน" เป็น "เริ่มต้น" (ปฏิเสธ + แก้ไขเนื่องจากวิธีนี้จะไม่ได้รับการอนุมัติจากผู้อนุมัติ robo หรือเพียงแค่หมุนการเปลี่ยนแปลงกลับหากได้รับการอนุมัติและคุณไม่ชอบ)
Martin Bonner สนับสนุน Monica

18

หากคุณกำลังตัดออกจากปลายสายที่dirnameอาจจะเหมาะกับการเรียกเก็บเงิน:

$ dirname xxxx/x/xx/xx/xxxx/x/yyyyy
xxxx/x/xx/xx/xxxx/x
$ _

echo /$(basename "$str")ถ้าคุณกำลังพยายามที่จะแยกส่วนสุดท้ายของสตริงที่ใช้

$ str=xxxx/x/xx/xx/xxxx/x/yyyyy
$ echo /$(basename "$str")
/yyyyy
$ _

3
สุจริตทำไมทุกคนจะแนะนำหรือยอมรับคำตอบที่เกี่ยวข้องกับ sed (หรือ awk หรือวิธีการที่คล้ายกันอื่น ๆ ) แทนที่จะใช้สาธารณูปโภคที่ติดตั้งกับระบบปฏิบัติการที่อยู่นอกเหนือฉัน
Auspex

@Auspex ฉันคิดว่ามันเป็นเพราะdirnameและbasenameไม่เป็นที่รู้จักอย่างกว้างขวาง แต่sedและawkเป็น
Volker Siegel

@Auspex: เนื่องจาก dirname และ basename ไม่ทำงานบน stdin ดังนั้นcat text | basenameจะไม่ทำงาน ทางออกที่เหมาะสมสำหรับการใช้คำถามของ OP dirnameและbasenameน่าจะเกี่ยวข้องxargsฯลฯ
Pod

16

การขยายพารามิเตอร์ใน bash

คุณสามารถใช้การขยายพารามิเตอร์bashในกรณีนี้

  • ${parameter%word}ที่wordเป็น/*
  • ${parameter##word}ที่wordเป็น*/

ตัวอย่าง:

ลบส่วนสุดท้าย

$ asdf="xxx/xxxx/xxxxx/yyy"
$ echo ${asdf%/*}
xxx/xxxx/xxxxx

นี่คือคำอธิบายในman bash:

${parameter%word}
${parameter%%word}
      Remove matching suffix pattern.  The word is expanded to produce
      a pattern just as in pathname expansion.  If the pattern matches
      a  trailing portion of the expanded value of parameter, then the
      result of the expansion is the expanded value of parameter  with
      the  shortest  matching  pattern (the ``%'' case) or the longest
      matching pattern (the ``%%'' case) deleted.  If parameter  is  @
      or  *,  the  pattern  removal operation is applied to each posi‐
      tional parameter in turn, and the  expansion  is  the  resultant
      list.   If  parameter is an array variable subscripted with @ or
      *, the pattern removal operation is applied to  each  member  of
      the array in turn, and the expansion is the resultant list.

ลบทั้งหมดยกเว้นส่วนสุดท้าย

$ asdf="xxx/xxxx/xxxxx/yyy"
$ echo ${asdf##*/}
yyy

คุณสามารถเพิ่มเครื่องหมายทับอย่างนั้น

$ echo /${asdf##*/}
/yyy

เพื่อให้ได้สิ่งที่คุณต้องการในอินสแตนซ์หนึ่งโดยเฉพาะตามคำถามที่แก้ไข แต่คำถามนี้ถูกแก้ไขโดยหลาย ๆ คนหลังจากนั้นและไม่ง่ายเลยที่จะรู้ว่าคุณต้องการอะไรในตอนนี้

นี่คือคำอธิบายในman bash:

${parameter#word}
${parameter##word}
      Remove matching prefix pattern.  The word is expanded to produce
      a pattern just as in pathname expansion.  If the pattern matches
      the  beginning of the value of parameter, then the result of the
      expansion is the expanded value of parameter with  the  shortest
      matching  pattern  (the ``#'' case) or the longest matching pat‐
      tern (the ``##'' case) deleted.  If parameter is  @  or  *,  the
      pattern  removal operation is applied to each positional parame‐
      ter in turn, and the expansion is the resultant list.  If param‐
      eter  is  an array variable subscripted with @ or *, the pattern
      removal operation is applied to each  member  of  the  array  in
      turn, and the expansion is the resultant list.

10

อีกวิธีหนึ่งคือใช้grepเพื่อแสดงเครื่องหมายสแลชสุดท้ายต่อบรรทัดเท่านั้นและอะไรก็ตามที่ตามมา:

$ grep -o '/[^/]*$' example.txt
/yyy
/yyyyy

คำอธิบาย:

-oบอกgrepให้แสดงเฉพาะส่วนที่ตรงกันของบรรทัดแทนทั้งบรรทัด

รูปแบบที่/[^/]*$ตรงทับตัวอักษร/ตามด้วยตัวอักษรใด ๆ ยกเว้นเฉือน[^/]จำนวนครั้งใดจนกว่าจะสิ้นสุดของบรรทัด*$


8

เพียงเพราะคนอื่นโพสต์คำตอบ“ มีสติ” มากกว่านี้นี่เป็นคำตอบที่ค่อนข้างงี่เง่า:

rev | cut -d/ -f1 | rev

revฝืนตัวอักษรในแต่ละบรรทัดเช่นกลายเป็นabcd/ef/g g/fe/dcbaจากนั้นcutตัดส่วนแรกออก ในที่สุดก็กลับรายการอีกครั้ง


2
แม้คำตอบนี้จะไม่ไร้เหตุผลโดยเฉพาะอย่างยิ่งเพราะการใช้งานของท่อประปาฉันชอบคำตอบนี้ :) ให้โยกไปเรื่อย ๆ +1
Sergiy Kolodyazhnyy

เห็นด้วย - ไม่ใช่คนมีสติ แต่เป็นคนเลว!
Volker Siegel

3

วิธีแก้ปัญหาแบบคลาสสิกawkที่ใช้/เป็นตัวคั่นฟิลด์สำหรับทั้งอินพุตและเอาต์พุตและตั้งค่าฟิลด์สุดท้ายเป็นสตริงว่าง (ซึ่งจริงๆคือ "การยกเลิกการลงทะเบียน" ของNFตัวแปรฟิลด์จำนวน):

$ echo "xxx/xxxx/xxxxx/yyy"|awk  'BEGIN{OFS=FS="/"};{$NF="";print $0}'
xxx/xxxx/xxxxx/

ที่สั้นกว่าในขณะที่fedorquiชี้ให้เห็นในความคิดเห็น:

$ echo "xxx/xxxx/xxxxx/yyy"|awk  'BEGIN{OFS=FS="/"};NF--'
xxx/xxxx/xxxxx/

การเปลี่ยนแปลงที่จะนำเส้นทางเข้าสู่awkสภาพแวดล้อมการทำงานของซึ่งจะประหยัดค่าใช้จ่ายในการประปา แต่ทำให้awkส่วน verbose เพิ่มเติม:

mypath="xxx/xxxx/xxxxx/yyy" awk  'BEGIN{OFS=FS="/"; sub(/\/([^\/]*)$/,"",ENVIRON["mypath"]);print(ENVIRON["mypath"]) };'

1
verbose เกินไปทำไมไม่เพียงแค่NF--? ในแบบที่คุณชอบprintbla bla นี้
fedorqui

3

กำลังเพิ่มคำตอบของ egmont เนื่องจากคำถามถูกแก้ไขแล้ว ...

ใช้--complementถ้าคุณต้องการที่จะลบข้อมูลครั้งแรกกับ-f1

echo "xxxx/x/xx/xx/xxxx/x/yyyyy"|rev|cut -d/ -f1 --complement|rev
xxxx/x/xx/xx/xxxx/x

echo "xxxx/x/xx/xx/xxxx/x/yyyyy"|rev|cut -d/ -f1|rev
yyyyy

นอกจากนี้คำถามยังไม่ชัดเจนเกี่ยวกับสิ่งที่ควรเกิดขึ้นกับอินพุตที่ไม่มีเครื่องหมายทับ xxxx => xxxx หรือ xxxx => ไม่มีอะไร


คุณสามารถแนะนำการแก้ไขได้: askubuntu.com/posts/1010356/edit
muru

@muru OP เป็นผู้ใช้ 1 คน การแก้ไขสิทธิ์ไม่ได้รับที่ 2k หรือไม่ askubuntu.com/help/privileges/edit
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy ทุกคนสามารถแนะนำการแก้ไขได้
muru

+1 - ฉันไม่ได้สังเกตเห็น--complementตัวเลือกก่อนหน้าและหน้าคนนั้นเป็นวงกลม การเติมเต็มหมายถึงการเติมเต็ม ผมพบว่าการกวดวิชาตัดที่ดีซึ่งครอบคลุมมันที่yourownlinux.com/2015/05/...มันเหมือนใน-v grepให้ฉันทุกอย่างยกเว้นสิ่งที่ตรงกัน
โจ

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