จะ 'วาง' / ลบตัวอักษรจากด้านหน้าของสตริงได้อย่างไร?


13

ฉันมีสตริงที่ฉันต้องการจัดการ สตริงเป็นH08W2345678วิธีที่ฉันจะสามารถจัดการกับมันดังนั้นการส่งออกเป็นเพียงแค่W2345678?

ในทำนองเดียวกันถ้าฉันต้องการที่จะลดตัวละคร 4 ตัวสุดท้ายจากH08W2345678นั้นฉันH08W234จะทำยังไงดี?


1
มีหลายวิธีในการจัดการสตริง มีเหตุผลเฉพาะในการใช้งานsedหรือไม่?
don_crissti

@don_crissti ไม่มีเหตุผลนอกจากขาดประสบการณ์ ทางเลือกใด ๆ ยินดีต้อนรับ ...
3kstc

@don_crissti เรื่องราว: จากไฟล์ CSV ที่กรองแล้วฉันใช้พารามิเตอร์ตัวใดตัวหนึ่งจากบรรทัดซึ่งเป็นH08W2345678และจำเป็นต้องจัดการกับW2345678ค่านี้ด้วยค่านี้เมื่อมีข้อมูลอื่นจะถูกใส่ลงในอีเมลที่ส่งออกไป การส่งอีเมลของพวกเขาจะถูกดำเนินการกับ cron
3kstc

@don_crissti awkไอเอ็นจีมัน ฉันสร้างอาเรย์แล้วปรับเปลี่ยนองค์ประกอบภายในอาเรย์ (แตกต่างกันทั้งหมด - นั่นคือเปลี่ยนการประทับเวลาของ Epoch ในไม่กี่วินาทีเป็นวันที่เป็นต้น)
3kstc

2
คุณสามารถทำสิ่งเช่นนั้นด้วย awk:printf %s\\n "XX,H08W2345678,YY" | awk -F, '{print substr($2, 4); print substr($2, 1, length($2)-4)}'
don_crissti

คำตอบ:


19

เพียงแค่ใช้ bash (หรือksh93ที่ที่มาจากหรือไวยากรณ์zsh):

string="H08W2345678"

echo "${string:3}"
W2345678

echo "${string:0:-4}"
H08W234

ดูวิกิพีเดีย Wooledge สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการจัดการสตริง


ต้องใช้ bash 4.2 หรือสูงกว่า ดูสำเนาเก่าของคู่มืออ้างอิง Bash ส่วน 3.5.3, '' การขยายพารามิเตอร์ของเชลล์ ''หรือคำตอบของลูกไก่ที่นี่เพื่อดูข้อ จำกัด เก่า (“ ความยาวต้องประเมินเป็นจำนวนที่มากกว่าหรือเท่ากับศูนย์”); … (ต่อ)
สกอตต์

(ต่อ) ... ดูการเปลี่ยนแปลงทุบตี (ที่ทุบตีแฮกเกอร์วิกิพีเดีย) (เลื่อนลงไปด้านล่างของส่วน) หรือข่าวทุบตีที่เทคโนโลยีองค์กรบริการโครงสร้างพื้นฐานที่มหาวิทยาลัย Case Western Reserve (ค้นหา“เพิ่มไปยังทุบตี-4.2” จากนั้นเลื่อนลงไปที่“ q.”) เพื่อดูการแก้ไข …………  "${string:0:${#string}-4}" ทำงานใน bash เวอร์ชั่น 4.1 ตราบใดที่ความยาว$stringอย่างน้อย 4
Scott

ป.ล. นี้จะทำให้หายใจไม่ออกในสตริงเช่นabc-eที่เมื่อคุณวางสามตัวแรกคุณจะเหลือ-e(เพราะecho -eไม่ได้ทำสิ่งที่คุณต้องการ)
สกอตต์

8
$ echo "H08W2345678" | sed 's/^.\{3\}//'
W2345678

sed 's/^.\{3\}//'จะค้นหาอักขระสามตัวแรกด้วย^.\{3\}และแทนที่ด้วยช่องว่าง ที่นี่^.จะจับคู่อักขระใด ๆ ที่จุดเริ่มต้นของสตริง ( ^ระบุการเริ่มต้นของสตริง) และ\{3\}จะจับคู่รูปแบบก่อนหน้านี้ 3 ครั้ง ดังนั้น^.\{3\}จะจับคู่สามตัวแรก

$ echo "H08W2345678" | sed 's/.\{4\}$//'
H08W234

ในทำนองเดียวกันsed 's/.\{4\}$//'จะแทนที่อักขระสี่ตัวสุดท้ายด้วยค่าว่าง ( $ระบุถึงจุดสิ้นสุดของสตริง)


1
คุณช่วยอธิบายได้ไหม's/^.\{3\}//'และ's/.\{4\}$//'ในขณะที่ฉันยังคงเรียนรู้สิ่งต่าง ๆ อยู่ขอบคุณมาก
3kstc

@ 3kstc: โปรดตรวจสอบการแก้ไข
heemayl

1
เพียงไม่กี่ตัวอักษรฉันต้องการใช้...แทน.\{3\}ตั้งแต่ (ให้ฉัน) มันง่ายต่อการอ่าน: หรือในการแสดงออกเดียวที่มีการสลับกัน:sed -e 's/^...//' -e 's/....$//' sed -r 's/^...|....$//g'ถ้ามันเป็นมากกว่าไม่กี่ตัวอักษรที่จะลบแล้วผมใช้การแสดงออกแทน/.\{17}\/ /.............../
จอห์นนี่

นี้จะประพฤติถ้าสตริงหรือ-e -nแน่นอนความหมายของ“วางสุดท้าย 4 ตัวอักษร” จะไม่ได้กำหนดสตริงสั้นกว่า 4 ตัวอักษร แต่ถ้าใครอยากจะปรับตัวนี้จะลดลงครั้งแรกหรือครั้งที่ผ่านมาหนึ่งในตัวละครที่มันจะระเบิดขึ้น
สกอตต์

2

หากคุณมีไฟล์ที่ทุกบรรทัดเป็นสตริงสิบเอ็ดตัวอักษร (หรืออะไรก็ตาม) ที่คุณต้องการสับ sedเป็นเครื่องมือที่ใช้ มันใช้ได้สำหรับการจัดการกับสายเดียว แต่มันเกินความจริง สำหรับสตริงเดี่ยวคำตอบของ Jasonน่าจะดีที่สุดถ้าคุณเข้าถึง bash เวอร์ชัน 4.2 หรือสูงกว่า อย่างไรก็ตาม และไวยากรณ์ที่ดูเหมือนจะไม่ซ้ำกันเพื่อทุบตี (ดีทุบตี ksh93, mksh และ zsh) - ฉันไม่เห็นพวกเขาในเปิดฐานกลุ่มข้อมูลจำเพาะของเชลล์ภาษาคำสั่ง หากคุณติดอยู่กับเชลล์ที่เข้ากับ POSIX ซึ่งไม่รองรับการขยายสตริงย่อย (การแตกไฟล์) คุณสามารถใช้${parameter:offset}${parameter:offset:length}

$ printf "%s\n" "${string#???}"
W2345678

$ printf "%s\n" "${string%????}"
H08W234

ใช้printfแทนechoการป้องกันสายเช่นabc-eที่ไหนเมื่อคุณวางอักขระสามตัวแรกคุณจะเหลือ-e (และecho -eไม่ทำสิ่งที่คุณต้องการ)

และหากคุณไม่ได้ใช้เชลล์เป้าหมายตระกูลบอร์นเลย (หรือคุณใช้ระบบ pre-POSIX เก่าแก่) สิ่งเหล่านี้ควรใช้งานได้:

$ expr " $string" : ' ...\(.*\)'
W2345678

$ expr " $string" : ' \(.*\)....'
H08W234

พื้นที่ชั้นนำพิเศษคือการหลีกเลี่ยงปัญหาที่มีค่าของ$string ที่เกิดขึ้นจริงexprผู้ประกอบการ (เช่น+,  /,  indexหรือmatch) หรือตัวเลือก (เช่น  --, --helpหรือ  --version)


@ Stéphane Chazelas: (1) ขอบคุณที่เตือนฉันถึงความผิดพลาดที่ฉันรู้เมื่อประมาณ 40 ปีที่แล้วและฉันก็ลืมมันได้ (2) ฉันมักจะแก้ปัญหานี้ด้วยX; เช่นexpr "X$string" : 'X...\(.*\)'. IMO นั้นง่ายต่อการอ่านและทำความเข้าใจ มีปัญหาใด ๆ กับเรื่องนั้นหรือเหตุผลใด ๆ ที่ต้องการพื้นที่หรือไม่? (3) วันนี้ฉันเรียนรู้ว่าexpr + "$string" : '...\(.*\)'ตอนนี้ใช้ได้ ฉันจำไม่ได้ว่าเมื่อ 40 ปีก่อน; มันใช้กันอย่างแพร่หลายเพียงพอที่จะปลอดภัยที่จะแนะนำ? (4) คุณพลาดข้อความจากคำตอบของ jasonwryan และ nit pick ในคำตอบของ heemayl
สกอตต์

AFAIK นั่นexpr +คือ GNU เท่านั้น (จะไม่ทำงานบน Solaris หรือ FreeBSD AFAICS) ผมใช้พื้นที่แทน x เป็นมันมีโอกาสน้อยที่ว่าบางส่วนexprการดำเนินงานจะมีผู้ประกอบการที่เริ่มต้นด้วยพื้นที่กว่าด้วยและเพราะมันเป็นโอกาสน้อยที่จะมีการเรียงองค์ประกอบที่เริ่มต้นด้วยพื้นที่กว่าด้วยx xแต่ฉันก็รู้ว่ามันอาจไม่ใช่ทางเลือกที่ดีสำหรับexpr " $a" "<" " $b"การเปรียบเทียบสตริงเนื่องจากการใช้งานบางอย่างสิ้นสุดการทำการเปรียบเทียบเชิงตัวเลขเมื่อ$a/ $bดูเหมือนว่าตัวเลข บางทีexpr "@@$a"...หรือexpr "x $a"อาจจะปลอดภัยกว่า
Stéphane Chazelas

0

ด้วย:

string="H08W2345678"

การจับคู่ 3 หรือ 4 ตัวอักษรดูเหมือนง่าย (สำหรับหอยส่วนใหญ่):

$ printf '%s\t%s\n' "${string#???}" "${string%????}"
W2345678      H08W234

สำหรับเชลล์ที่เก่ากว่า (เช่นเชลล์เป้าหมาย) ให้ใช้:

$ string=H08W2345678

$ expr " ${string}" : " ...\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\)...." '
H08W234

หากจำเป็นต้องใช้จำนวนอักขระให้ใช้:

$ expr " ${string}" : " .\{3\}\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\).\{4\}" '
H08W234

แน่นอนว่า regex เหล่านั้นทำงานด้วย sed, awk และ bash 3.0+:

$ echo "$string" | sed 's/^.\{3\}//'
W2345678

$ echo "$string" | sed 's/.\{4\}$//'
H08W234

$ echo "$string" | awk '{sub(/^.{3}/,"")}1'
W2345678

$ echo "$string" | awk '{sub(/.{4}$/,"")}1'
H08W234

$ r='^.{3}(.*)$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
W2345678

$ r='^(.*).{4}$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
H08W234

-1

จะ 'วาง' / ลบตัวอักษรจากด้านหน้าของสตริงได้อย่างไร?

ฉันมีสตริงที่ฉันต้องการจัดการ สตริงคือ H08W2345678 ฉันจะสามารถจัดการมันได้อย่างไรดังนั้นเอาต์พุตจึงเป็นเพียง W2345678

echo "H08W2345678" | cut -c 4-

นี่ตอบเพียงครึ่งหนึ่งของคำถาม
Kusalananda

ฉันเชื่อว่าการลงคะแนนเสียงของคุณไม่ยุติธรรม ครึ่งนี้ตอบคำถามที่ฉันมีเมื่อฉัน googled posix ลบอักขระแรกและหน้านี้ปรากฏในผลการค้นหา นอกจากนี้ชื่อหน้านี้ครอบคลุมเฉพาะครึ่งหนึ่งของคำถาม ฉันกลับมาและมีส่วนร่วมเมื่อฉันพบวิธีการแก้ปัญหาที่ฉันชอบ - ฉันคิดว่าสำหรับงานcutนั้นมีความงดงามมากกว่าสิ่งอื่นใดในหน้านี้
aexl
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.