แยกสตริงย่อยใน Bash


728

ให้ชื่อไฟล์ในรูปแบบsomeletters_12345_moreleters.extฉันต้องการแยก 5 หลักและใส่ลงในตัวแปร

ดังนั้นเพื่อเน้นจุดนั้นฉันมีชื่อไฟล์ที่มีจำนวน x ตัวอักษรจากนั้นลำดับห้าหลักที่ล้อมรอบด้วยขีดล่างเดี่ยวด้านใดด้านหนึ่งแล้วอีกชุดของจำนวน x อักขระ ฉันต้องการที่จะนำตัวเลข 5 หลักและใส่ลงในตัวแปร

ฉันสนใจมากในหลายวิธีที่สามารถทำได้


5
คำตอบของ JB ชัดเจนว่าได้รับคะแนน - เวลาเปลี่ยนคำตอบที่ยอมรับแล้วหรือไม่
Jeff

3
คำตอบส่วนใหญ่ดูเหมือนจะไม่ตอบคำถามของคุณเพราะคำถามนั้นคลุมเครือ "ผมมีชื่อไฟล์ที่มีจำนวน x ของตัวละครแล้วลำดับห้าหลักที่ล้อมรอบด้วยขีดเดียวในด้านใดด้านหนึ่งแล้วชุดของ x จำนวนของตัวละครอีก" โดยคำจำกัดความที่abc_12345_def_67890_ghi_defเป็นอินพุตที่ถูกต้อง คุณต้องการจะเกิดอะไรขึ้น สมมติว่ามีลำดับ 5 หลักเดียวเท่านั้น คุณยังมีabc_def_12345_ghi_jklหรือ1234567_12345_1234567หรือ12345d_12345_12345eเป็นข้อมูลที่ถูกต้องขึ้นอยู่กับความหมายของการป้อนข้อมูลและส่วนใหญ่ของคำตอบด้านล่างจะไม่จัดการนี้
gman

2
คำถามนี้มีตัวอย่างอินพุตที่เฉพาะเจาะจงเกินไป ด้วยเหตุนี้จึงมีคำตอบเฉพาะสำหรับกรณีนี้จำนวนมาก(ตัวเลขเท่านั้น_ตัวคั่นเดียวกันอินพุตที่มีสตริงเป้าหมายเพียงครั้งเดียวเป็นต้น) ที่ดีที่สุด (ทั่วไปมากที่สุดและเร็วที่สุด) คำตอบที่มีหลังจาก 10 ปีเพียง 7 upvotes ในขณะที่คำตอบอื่น ๆ ที่ จำกัด มีหลายร้อย ทำให้ฉันหมดความเชื่อมั่นในนักพัฒนา
😞

คำตอบ:


691

ใช้ตัด :

echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2

ทั่วไปเพิ่มเติม:

INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING

1
คำตอบทั่วไปมากขึ้นเป็นสิ่งที่ฉันถูกมองหาขอบคุณ
Berek ไบรอัน

71
แฟล็ก -f ใช้ดัชนีที่อิง 1 แทนที่จะเป็นดัชนี 0 ที่โปรแกรมเมอร์จะถูกใช้
Matthew G

2
INPUT = someletters_12345_moreleters.ext SUBSTRING = $ (echo $ INPUT | cut -d'_ '-f 2) echo $ SUBSTRING
mani deepak

3
คุณควรใช้เครื่องหมายอัญประกาศคู่ล้อมรอบอาร์กิวเมนต์อย่างถูกต้องechoเว้นแต่คุณจะทราบอย่างแน่นอนว่าตัวแปรไม่สามารถมีช่องว่างที่ผิดปกติหรือ metacharacters เชลล์ ดูstackoverflow.com/questions/10067266/
tripleee

หมายเลข '2' after '-f' คือการบอก shell เพื่อแยกชุดย่อยที่ 2
Sandun

1086

หากxเป็นค่าคงที่การขยายพารามิเตอร์ต่อไปนี้จะทำการแยกสตริงย่อย:

b=${a:12:5}

โดยที่12คือออฟเซ็ต (อิงตามศูนย์) และ5คือความยาว

หากขีดล่างรอบตัวเลขเป็นเพียงตัวเลขเดียวในอินพุตคุณสามารถตัดส่วนนำหน้าและส่วนต่อท้าย (ตามลำดับ) ออกเป็นสองขั้นตอน:

tmp=${a#*_}   # remove prefix ending in "_"
b=${tmp%_*}   # remove suffix starting with "_"

หากมีขีดล่างอื่น ๆ ก็น่าจะเป็นไปได้อยู่ดี หากใครรู้วิธีการขยายทั้งสองอย่างในนิพจน์เดียวฉันก็อยากจะรู้เช่นกัน

โซลูชันทั้งสองที่นำเสนอนั้นเป็น bash ที่บริสุทธิ์โดยไม่มีการวางไข่ในกระบวนการดังนั้นจึงรวดเร็วมาก


18
@SpencerRathbun bash: ${${a#*_}%_*}: bad substitutionบน GNU ของฉันทุบตี 4.2.45
JB

2
@ JonnyB, บางครั้งในอดีตที่ทำงาน ฉันได้รับคำสั่งจากเพื่อนร่วมงานของฉันว่ามันหยุดทำงานและพวกเขาเปลี่ยนเป็นคำสั่งหรืออะไรก็ตาม เมื่อดูในประวัติศาสตร์ฉันใช้มันในshสคริปต์ซึ่งอาจเป็นเส้นประ ณ จุดนี้ฉันไม่สามารถทำงานได้อีกต่อไป
Spencer Rathbun

22
JB คุณควรชี้แจงว่า "12" คือออฟเซ็ต (อิงเป็นศูนย์) และ "5" คือความยาว และ +1 สำหรับลิงก์ของ @gontard ที่ให้ทุกอย่างออกไป!
Doktor J

1
ขณะที่เรียกใช้สิ่งนี้ภายในสคริปต์ในชื่อ "sh run.sh" อาจมีข้อผิดพลาดในการทดแทนที่ไม่ดี เพื่อหลีกเลี่ยงปัญหานี้ให้เปลี่ยนการอนุญาตสำหรับ run.sh (chmod + x run.sh) จากนั้นเรียกใช้สคริปต์ในชื่อ "./run.sh"
Ankur

2
ค่าชดเชย offset อาจเป็นลบได้เช่นกัน คุณเพียงแค่ต้องระวังไม่ให้กาวติดกับลำไส้ใหญ่มิฉะนั้นทุบตีจะตีความว่าเป็นการ:-ทดแทน "ใช้ค่าเริ่มต้น" ให้${a: -12:5}ผลผลิตอักขระ 5 ตัว 12 ตัวจากจุดสิ้นสุดและ${a: -12:-5}ตัวอักษร 7 ตัวระหว่าง end-12 และ end-5
JB

97

วิธีการแก้ปัญหาทั่วไปที่หมายเลขสามารถอยู่ที่ใดก็ได้ในชื่อไฟล์โดยใช้ลำดับแรกของดังกล่าว:

number=$(echo $filename | egrep -o '[[:digit:]]{5}' | head -n1)

อีกวิธีในการแยกส่วนของตัวแปร:

number=${filename:offset:length}

หากชื่อไฟล์ของคุณมีรูปแบบอยู่เสมอstuff_digits_...คุณสามารถใช้ awk:

number=$(echo $filename | awk -F _ '{ print $2 }')

อีกวิธีหนึ่งในการลบทุกอย่างยกเว้นตัวเลขให้ใช้

number=$(echo $filename | tr -cd '[[:digit:]]')

2
ถ้าฉันต้องการแยกตัวเลข / คำจากบรรทัดสุดท้ายของไฟล์
Sahra

93

ลองใช้ดู cut -c startIndx-stopIndx


2
มีบางอย่างเช่น startIndex-lastIndex - 1 หรือไม่
Niklas

1
@Niklas ในทุบตี proly startIndx-$((lastIndx-1))
brown.2179

3
start=5;stop=9; echo "the rain in spain" | cut -c $start-$(($stop-1))
brown.2179

1
ปัญหาคืออินพุตเป็นแบบไดนามิกเนื่องจากฉันยังใช้ไพพ์เพื่อให้ได้ดังนั้นโดยทั่วไป git log --oneline | head -1 | cut -c 9-(end -1)
Niklas

นี้สามารถทำได้ด้วยการตัดถ้าแบ่งออกเป็นสองส่วนเป็นline=คอมไพล์บันทึก - ออนไลน์ หัว -1` && echo $ line | ตัด -c 9 - $ (($ {# line} -1)) `แต่ในกรณีนี้โดยเฉพาะอาจจะดีกว่าที่จะใช้sed asgit log --oneline | head -1 | sed -e 's/^[a-z0-9]* //g'
brown.2179

34

ในกรณีที่มีคนต้องการข้อมูลที่เข้มงวดมากขึ้นคุณสามารถค้นหาในคนทุบตีเช่นนี้

$ man bash [press return key]
/substring  [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]

ผลลัพธ์:

$ {พารามิเตอร์: ชดเชย}
       $ {พารามิเตอร์: offset: ความยาว}
              การขยายย่อย ขยายความยาวสูงสุดอักขระของ
              พารามิเตอร์เริ่มต้นที่ตัวละครที่ระบุโดยชดเชย ถ้า
              ละเว้นความยาวแล้วขยายไปยังสตริงย่อยของพารามิเตอร์ start‐
              ไอเอ็นจีที่ตัวละครที่ระบุโดยชดเชย ความยาวและชดเชยคือ
              นิพจน์ทางคณิตศาสตร์ (ดูการประเมิน ARITHMETIC ด้านล่าง) ถ้า
              ชดเชยประเมินจำนวนน้อยกว่าศูนย์ค่าที่ใช้
              เป็นการชดเชยจากจุดสิ้นสุดของค่าพารามิเตอร์ คณิตศาสตร์
              การแสดงออกที่เริ่มต้นด้วย - จะต้องคั่นด้วยช่องว่าง
              จากก่อนหน้านี้: จะแตกต่างจากใช้ค่าเริ่มต้น
              การขยายค่า หากความยาวประเมินเป็นจำนวนที่น้อยกว่า
              ศูนย์และพารามิเตอร์ไม่ใช่ @ และไม่ใช่ดัชนีหรือการเชื่อมโยง
              อาร์เรย์มันถูกตีความว่าเป็นชดเชยจากจุดสิ้นสุดของค่า
              ของพารามิเตอร์มากกว่าจำนวนตัวอักษรและการขยาย
              sion คืออักขระระหว่างสองออฟเซ็ต หากพารามิเตอร์คือ
              @ ผลลัพธ์คือพารามิเตอร์ความยาวตำแหน่งเริ่มต้นที่ปิด
              ชุด ถ้าพารามิเตอร์เป็นชื่ออาร์เรย์ที่จัดทำดัชนีห้อยโดย @ หรือ
              * ผลลัพธ์คือสมาชิกความยาวของอาร์เรย์ที่ขึ้นต้นด้วย
              $ {พารามิเตอร์ [ชดเชย]} อ็อฟเซ็ตเชิงลบถูกนำมาเทียบกับ
              หนึ่งมากกว่าดัชนีสูงสุดของอาร์เรย์ที่ระบุ อนุ
              การขยายสตริงที่ใช้กับอาเรย์แบบเชื่อมโยงสร้างการไม่ได้ผล
              ผลการปรับ โปรดทราบว่าจะต้องแยกออฟเซ็ตเชิงลบ
              จากลำไส้ใหญ่อย่างน้อยหนึ่งช่องว่างเพื่อหลีกเลี่ยงการสับสน
              ด้วย: - การขยายตัว การจัดทำดัชนี substring นั้นเป็นแบบ zero-based เว้นแต่
              ใช้พารามิเตอร์ตำแหน่งซึ่งในกรณีนี้การจัดทำดัชนี
              เริ่มต้นที่ 1 โดยค่าเริ่มต้น ถ้า offset เป็น 0 และตำแหน่ง
              มีการใช้พารามิเตอร์ $ 0 นำหน้าไปยังรายการ

2
ข้อแม้สำคัญมากที่มีค่าลบตามที่ระบุไว้ข้างต้น: นิพจน์ทางคณิตศาสตร์ที่ขึ้นต้นด้วย - จะต้องคั่นด้วยช่องว่างจากก่อนหน้านี้: จะต้องแตกต่างจากส่วนขยายใช้ค่าเริ่มต้น ดังนั้นเพื่อให้ได้ข้อเสนอนี้อยู่สี่ตัวอักษรของ var A:${var: -4}
sshow

26

นี่คือวิธีที่ฉันจะทำ:

FN=someletters_12345_moreleters.ext
[[ ${FN} =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}

คำอธิบาย:

ทุบตีเฉพาะ:

นิพจน์ทั่วไป (RE): _([[:digit:]]{5})_

  • _ เป็นตัวอักษรเพื่อแบ่งเขต / จับคู่ขอบเขตการจับคู่สำหรับสตริงที่ถูกจับคู่
  • () สร้างกลุ่มจับภาพ
  • [[:digit:]] เป็นคลาสตัวละครฉันคิดว่ามันพูดเพื่อตัวเอง
  • {5} หมายถึงตัวอักษรห้าตัวก่อนหน้าคลาส (ดังในตัวอย่างนี้) หรือกลุ่มต้องตรงกัน

ในภาษาอังกฤษคุณสามารถคิดได้ว่ามันทำงานแบบนี้: FNสตริงเป็นตัวอักษรซ้ำแล้วซ้ำอีกจนกว่าเราจะเห็น_จุดที่กลุ่มการจับกุมถูกเปิดขึ้นและเราพยายามจับคู่ห้าหลัก หากการจับคู่นั้นประสบความสำเร็จจนถึงจุดนี้กลุ่มการดักจับจะบันทึกการเดินทางห้าหลัก หากอักขระถัดไปเป็น_เงื่อนไขเงื่อนไขสำเร็จกลุ่มการดักจับพร้อมใช้งานในBASH_REMATCHและNUM=คำสั่งถัดไปสามารถดำเนินการได้ _ถ้าเป็นส่วนหนึ่งของการจับคู่ใดไม่รายละเอียดที่บันทึกไว้จะถูกกำจัดและตัวอักษรโดยการประมวลผลตัวละครอย่างต่อเนื่องหลังจากที่ เช่นถ้าFNที่ไหน_1 _12 _123 _1234 _12345_จะมีการเริ่มต้นที่ผิดพลาดสี่ครั้งก่อนที่จะพบการแข่งขัน


3
นี่เป็นวิธีทั่วไปที่ใช้งานได้แม้ว่าคุณจะต้องแยกสิ่งต่าง ๆ มากกว่าที่ฉันทำ
zebediah49

3
นี่เป็นคำตอบที่ธรรมดาที่สุดและควรได้รับการยอมรับ มันใช้งานได้สำหรับนิพจน์ปกติไม่ใช่แค่สตริงอักขระที่ตำแหน่งคงที่หรือระหว่างตัวคั่นเดียวกัน (ซึ่งเปิดใช้งานcut) นอกจากนี้ยังไม่พึ่งพาการดำเนินการคำสั่งภายนอก
Dan Dascalescu

1
คำตอบนี้ไม่ถูกเปิดเผยทางอาญา
chepner

มันเยี่ยมมาก! ฉันปรับใช้สิ่งนี้เพื่อใช้ดิจิมิเตอร์เริ่ม / หยุดที่แตกต่างกัน (แทนที่ _) และหมายเลขความยาวผันแปร (. สำหรับ {5}) สำหรับสถานการณ์ของฉัน บางคนสามารถทำลายเวทมนตร์ดำนี้และอธิบายมันได้หรือไม่
พอล

1
@Paul ฉันเพิ่มรายละเอียดเพิ่มเติมในคำตอบของฉัน หวังว่าจะช่วย
nicerobot

21

ฉันประหลาดใจที่โซลูชันทุบตีบริสุทธิ์นี้ไม่ได้เกิดขึ้น:

a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345

คุณอาจต้องการรีเซ็ต IFS เป็นค่าที่เคยมีมาก่อนหรือunset IFSหลังจากนั้น!


1
มันไม่ได้แก้ปัญหาทุบตีบริสุทธิ์ผมคิดว่ามันทำงานในเปลือกบริสุทธิ์ (bin / sh /)
Kayn

5
+1 คุณสามารถเขียนนี้วิธีที่จะหลีกเลี่ยงการล้างอีกIFSพารามิเตอร์และตำแหน่ง:IFS=_ read -r _ digs _ <<< "$a"; echo "$digs"
Kojiro

2
นี่คือการขยายชื่อพา ธ ! (ดังนั้นมันเสีย)
gniourf_gniourf

20

สร้างคำตอบของ jor (ซึ่งไม่ได้ผลสำหรับฉัน):

substring=$(expr "$filename" : '.*_\([^_]*\)_.*')

12
นิพจน์ทั่วไปเป็นข้อตกลงที่แท้จริงเมื่อคุณมีบางสิ่งที่ซับซ้อนและการนับขีดล่างจะไม่cutเกิดขึ้น
Aleksandr Levchuk

12

ปฏิบัติตามข้อกำหนด

ฉันมีชื่อไฟล์ที่มีจำนวนตัวอักษร x จากนั้นลำดับที่ห้าหลักล้อมรอบด้วยเครื่องหมายขีดล่างเดี่ยวด้านใดด้านหนึ่งแล้วอีกจำนวนชุดอักขระ x ฉันต้องการที่จะนำตัวเลข 5 หลักและใส่ลงในตัวแปร

ฉันพบgrepวิธีการบางอย่างที่อาจเป็นประโยชน์:

$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+" 
12345

หรือดีกว่า

$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}" 
12345

แล้วด้วย-Poไวยากรณ์:

$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d+' 
12345

หรือถ้าคุณต้องการทำให้มันพอดี 5 ตัวอักษร:

$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d{5}' 
12345

ในที่สุดเพื่อให้มันถูกเก็บไว้ในตัวแปรมันก็แค่ต้องใช้var=$(command)ไวยากรณ์


2
ฉันเชื่อว่าทุกวันนี้ไม่จำเป็นต้องใช้ egrep คำสั่งเตือนคุณ: Invocation as 'egrep' is deprecated; use 'grep -E' instead. ฉันแก้ไขคำตอบของคุณแล้ว
สารสื่อประสาท

11

หากเรามุ่งเน้นในแนวคิดของ:
"ตัวเลข (หนึ่งหรือหลายหลัก)"

เราสามารถใช้เครื่องมือภายนอกหลายอย่างเพื่อแยกตัวเลข
เราสามารถลบตัวละครอื่น ๆ ได้อย่างง่ายดายไม่ว่าจะ sed หรือ tr:

name='someletters_12345_moreleters.ext'

echo $name | sed 's/[^0-9]*//g'    # 12345
echo $name | tr -c -d 0-9          # 12345

แต่ถ้า $ name มีตัวเลขหลายตัวการดำเนินการด้านบนจะล้มเหลว:

หาก "name = someletters_12345_moreleters_323_end.ext" ดังนั้น:

echo $name | sed 's/[^0-9]*//g'    # 12345323
echo $name | tr -c -d 0-9          # 12345323

เราจำเป็นต้องใช้การแสดงออกปกติ (regex)
ในการเลือกเฉพาะการวิ่งครั้งแรก (12345 ไม่ใช่ 323) ในแบบ sed และ perl:

echo $name | sed 's/[^0-9]*\([0-9]\{1,\}\).*$/\1/'
perl -e 'my $name='$name';my ($num)=$name=~/(\d+)/;print "$num\n";'

แต่เราสามารถทำได้โดยตรงใน bash (1) :

regex=[^0-9]*([0-9]{1,}).*$; \
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}

สิ่งนี้ช่วยให้เราสามารถดึงตัวเลขหลักที่มีความยาวไม่เกิน
ล้อมรอบด้วยข้อความ / อักขระอื่น ๆ

หมายเหตุ : regex=[^0-9]*([0-9]{5,5}).*$;จะตรงกับการทำงาน 5 หลักเท่านั้น :-)

(1) : เร็วกว่าการเรียกเครื่องมือภายนอกสำหรับข้อความสั้น ๆ แต่ละข้อความ ไม่เร็วกว่าการประมวลผลทั้งหมดภายใน sed หรือ awk สำหรับไฟล์ขนาดใหญ่


10

หากไม่มีกระบวนการย่อยใด ๆ คุณสามารถ:

shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}

ตัวแปรเล็ก ๆ นี้จะทำงานใน ksh93


9

ต่อไปนี้เป็นคำนำหน้าคำต่อท้าย (คล้ายกับโซลูชันที่กำหนดโดย JB และ Darron) ที่ตรงกับบล็อกแรกของตัวเลขและไม่ขึ้นอยู่กับขีดล่าง:

str='someletters_12345_morele34ters.ext'
s1="${str#"${str%%[[:digit:]]*}"}"   # strip off non-digit prefix from str
s2="${s1%%[^[:digit:]]*}"            # strip off non-digit suffix from s1
echo "$s2"                           # 12345

7

ฉันชอบsedความสามารถในการจัดการกับกลุ่ม regex:

> var="someletters_12345_moreletters.ext"
> digits=$( echo $var | sed "s/.*_\([0-9]\+\).*/\1/p" -n )
> echo $digits
12345

ตัวเลือกที่เล็กน้อยทั่วไปมากขึ้นจะไม่ได้ที่จะคิดว่าคุณมีขีดลายจุดเริ่มต้นของลำดับตัวเลขของคุณด้วยเหตุนี้เช่นลอกออกทั้งหมดตัวเลขที่ไม่ใช่คุณจะได้รับก่อนที่จะลำดับของคุณ:_s/[^0-9]\+\([0-9]\+\).*/\1/p


> man sed | grep s/regexp/replacement -A 2
s/regexp/replacement/
    Attempt to match regexp against the pattern space.  If successful, replace that portion matched with replacement.  The replacement may contain the special  character  &  to
    refer to that portion of the pattern space which matched, and the special escapes \1 through \9 to refer to the corresponding matching sub-expressions in the regexp.

เพิ่มเติมเกี่ยวกับเรื่องนี้ในกรณีที่คุณไม่มั่นใจกับ regexps มากเกินไป:

  • s สำหรับ _s_ubstitute
  • [0-9]+ ตรงกับ 1+ หลัก
  • \1 ลิงก์ไปยังกลุ่ม n.1 ของเอาต์พุต regex (กลุ่ม 0 คือการแข่งขันทั้งหมดกลุ่ม 1 คือการแข่งขันภายในวงเล็บในกรณีนี้)
  • p การตั้งค่าสถานะสำหรับ _p_rinting

escapes ทั้งหมด\อยู่ที่นั่นเพื่อให้sedการประมวลผล regexp ทำงานได้


6

คำตอบของฉันจะมีการควบคุมสิ่งที่คุณต้องการจากสายของคุณ นี่คือรหัสเกี่ยวกับวิธีที่คุณสามารถแยก12345สตริงออก

str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str

นี้จะมีประสิทธิภาพมากขึ้นถ้าคุณต้องการที่จะดึงสิ่งที่มีตัวอักษรใด ๆ เช่นabcหรือตัวอักษรพิเศษใด ๆ เช่นหรือ_ -ตัวอย่างเช่น: ถ้าสตริงของคุณเป็นเช่นนี้และคุณต้องการทุกอย่างที่อยู่หลังsomeletters_และก่อนหน้า_moreleters.ext:

str="someletters_123-45-24a&13b-1_moreleters.ext"

ด้วยรหัสของฉันคุณสามารถพูดถึงสิ่งที่คุณต้องการ คำอธิบาย:

#*มันจะลบสตริงก่อนหน้ารวมถึงคีย์การจับคู่ นี่คือกุญแจที่เราพูดถึง_ %มันจะลบสตริงต่อไปนี้รวมถึงคีย์ที่ตรงกัน กุญแจสำคัญที่เราพูดถึงคือ '_more *'

ทำการทดลองด้วยตัวเองและคุณจะพบว่าสิ่งนี้น่าสนใจ


6

รับ test.txt เป็นไฟล์ที่มี "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

cut -b19-20 test.txt > test1.txt # This will extract chars 19 & 20 "ST" 
while read -r; do;
> x=$REPLY
> done < test1.txt
echo $x
ST

นี่เป็นสิ่งที่เฉพาะเจาะจงมากสำหรับอินพุตนั้น เพียงการแก้ปัญหาทั่วไปของคำถามทั่วไป (ซึ่ง OP ควรจะได้ถาม) คือการใช้กับ regexp
Dan Dascalescu

3

ตกลงที่นี่ไปทดแทนพารามิเตอร์บริสุทธิ์ด้วยสตริงที่ว่างเปล่า ข้อแม้คือฉันได้กำหนดsomelettersและmorelettersเป็นอักขระเท่านั้น หากเป็นตัวอักษรและตัวเลขสิ่งนี้จะไม่ทำงานอย่างที่มันเป็น

filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345

2
ยอดเยี่ยม แต่ต้องการอย่างน้อย bash v4
olibre

2

คล้ายกับ substr ('abcdefg', 2-1, 3) ใน php:

echo 'abcdefg'|tail -c +2|head -c 3

นี่เป็นสิ่งที่เฉพาะเจาะจงมากสำหรับอินพุตนั้น เพียงการแก้ปัญหาทั่วไปของคำถามทั่วไป (ซึ่ง OP ควรจะได้ถาม) คือการใช้กับ regexp
Dan Dascalescu

1

นอกจากนี้ยังมีคำสั่ง bash builtin 'expr':

INPUT="someletters_12345_moreleters.ext"  
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `  
echo $SUBSTRING

4
exprไม่ใช่ builtin
gniourf_gniourf

1
นอกจากนี้ยังไม่จำเป็นในแง่ของผู้ประกอบการได้รับการสนับสนุนโดย=~ [[
chepner

1

สายไปหน่อย แต่ฉันเพิ่งพบปัญหานี้และพบสิ่งต่อไปนี้:

host:/tmp$ asd=someletters_12345_moreleters.ext 
host:/tmp$ echo `expr $asd : '.*_\(.*\)_'`
12345
host:/tmp$ 

ฉันใช้เพื่อรับความละเอียดมิลลิวินาทีในระบบฝังตัวที่ไม่มี% N สำหรับวันที่:

set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction

1

วิธีทุบตี:

IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'

xนี้จะบังคับตัวแปรที่เรียกว่า วาอาจมีการเปลี่ยนแปลงไปวาx_

input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"

1

สิ้นสุด Inklusive คล้ายกับการใช้ JS และ Java ลบ +1 หากคุณไม่ต้องการสิ่งนี้

substring() {
    local str="$1" start="${2}" end="${3}"

    if [[ "$start" == "" ]]; then start="0"; fi
    if [[ "$end"   == "" ]]; then end="${#str}"; fi

    local length="((${end}-${start}+1))"

    echo "${str:${start}:${length}}"
} 

ตัวอย่าง:

    substring 01234 0
    01234
    substring 012345 0
    012345
    substring 012345 0 0
    0
    substring 012345 1 1
    1
    substring 012345 1 2
    12
    substring 012345 0 1
    01
    substring 012345 0 2
    012
    substring 012345 0 3
    0123
    substring 012345 0 4
    01234
    substring 012345 0 5
    012345

ตัวอย่างการโทรเพิ่มเติม:

    substring 012345 0
    012345
    substring 012345 1
    12345
    substring 012345 2
    2345
    substring 012345 3
    345
    substring 012345 4
    45
    substring 012345 5
    5
    substring 012345 6

    substring 012345 3 5
    345
    substring 012345 3 4
    34
    substring 012345 2 4
    234
    substring 012345 1 3
    123

ยินดี.

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