ฉันต้องการลบอักขระตัวสุดท้ายของสตริงฉันลองใช้สคริปต์ตัวน้อยนี้:
#! /bin/sh
t="lkj"
t=${t:-2}
echo $t
แต่มันพิมพ์ "lkj" สิ่งที่ฉันทำผิด?
ฉันต้องการลบอักขระตัวสุดท้ายของสตริงฉันลองใช้สคริปต์ตัวน้อยนี้:
#! /bin/sh
t="lkj"
t=${t:-2}
echo $t
แต่มันพิมพ์ "lkj" สิ่งที่ฉันทำผิด?
คำตอบ:
ในเปลือก POSIX ไวยากรณ์${t:-2}
หมายถึงบางสิ่งบางอย่างที่แตกต่างกัน - มันจะขยายมูลค่าของt
ถ้าt
มีการตั้งค่าและไม่โมฆะและอื่น ๆ 2
ค่า หากต้องการตัดอักขระเดี่ยวตามการขยายพารามิเตอร์ไวยากรณ์ที่คุณอาจต้องการคือ${t%?}
โปรดสังเกตว่าในksh93
, bash
หรือzsh
, ${t:(-2)}
หรือ${t: -2}
(หมายเหตุพื้นที่) มีกฎหมายการขยายตัวย่อย แต่อาจจะไม่ใช่สิ่งที่คุณต้องการตั้งแต่พวกเขากลับมาย่อยเริ่มต้นที่ตำแหน่ง 2 ตัวละครในจากปลาย (คือมันเอาครั้งแรกที่ตัวละครi
ของ สตริงijk
)
ดูส่วนการขยายพารามิเตอร์เชลล์ของคู่มืออ้างอิง Bash สำหรับข้อมูลเพิ่มเติม:
${parameter%word}
ลบการจับคู่รูปแบบคำต่อท้ายที่สั้นที่สุดword
- ดูส่วนการขยายพารามิเตอร์ของman bash
ด้วยbash
4.2 ขึ้นไปคุณสามารถทำสิ่งต่อไปนี้
${var::-1}
ตัวอย่าง:
$ a=123
$ echo "${a::-1}"
12
โปรดสังเกตว่าสำหรับรุ่นเก่าbash
(เช่นbash 3.2.5
ใน OS X) คุณควรเว้นช่องว่างระหว่างและหลังเครื่องหมายโคลอน:
${var: : -1}
bash
เวอร์ชัน 4.2-alpha และสูงกว่าแย่กว่าที่ฉันมีในรุ่นก่อนหน้านี้ : - /
${var:offset:lenght}
bash 4.2
บางที OSX bash
เพิ่มแพทช์ของตัวเองสำหรับ
สำหรับการลบn
อักขระสุดท้ายออกจากบรรทัดที่ไม่ใช้sed
OR awk
:
> echo lkj | rev | cut -c (n+1)- | rev
ตัวอย่างเช่นคุณสามารถลบอักขระตัวสุดท้ายone character
โดยใช้สิ่งนี้:
> echo lkj | rev | cut -c 2- | rev
> lk
จากrev
manpage:
DESCRIPTION
ยูทิลิตี้ rev จะคัดลอกไฟล์ที่ระบุไปยังเอาต์พุตมาตรฐานโดยกลับลำดับของอักขระในทุกบรรทัด หากไม่มีไฟล์ที่ระบุอินพุตมาตรฐานจะถูกอ่าน
UPDATE:
หากคุณไม่ทราบความยาวของสตริงลอง:
$ x="lkj"
$ echo "${x%?}"
lk
ใช้ sed มันควรจะเร็วเท่า
sed 's/.$//'
เดียวของคุณก้องecho ljk | sed 's/.$//'
เป็นแล้ว
เมื่อใช้สิ่งนี้สตริง 1 บรรทัดอาจมีขนาดใดก็ได้
ตัวเลือกบางอย่างขึ้นอยู่กับเปลือก:
t=${t%?}
t=`expr " $t" : ' \(.*\).'`
t=${t[1,-2]}
t=${t:0:-1}
t=${t:0:${#t}-1}
t=${t/%?}
t=${t/~(E).$/}
@ {t=$1} ~~ $t *?
โปรดทราบว่าในขณะที่ทุกคนควรจะดึงตัวอักษรตัวสุดท้ายออกมาคุณจะพบว่าการใช้งานบางอย่าง (ที่ไม่รองรับตัวอักษรหลายไบต์) ดึงไบต์สุดท้ายแทน (ดังนั้นน่าจะทำให้ตัวอักษรตัวสุดท้ายเสีย )
ตัวexpr
แปรผันถือว่า$t
ไม่ได้ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่มากกว่าหนึ่งตัว นอกจากนี้ยังจะกลับมาเป็นสถานะทางออกที่ไม่ใช่ศูนย์ถ้าสตริงส่งผลให้สิ้นสุดขึ้นเป็น0
(หรือ000
หรือแม้กระทั่ง-0
กับการใช้งานบางส่วน) นอกจากนี้ยังอาจให้ผลลัพธ์ที่ไม่คาดคิดหากสตริงมีอักขระที่ไม่ถูกต้อง
t=${t%?}
ไม่ใช่ Bourne แต่คุณไม่น่าจะเจอกับ Bourne shell ในปัจจุบัน ${t%?}
ทำงานในอื่น ๆ ทั้งหมดแม้ว่า
fish
กำลังทำงาน 2.3.0 ซึ่งนำเสนอstring
บิลด์อินนั้นไม่ได้ถูกปล่อยออกมาในช่วงเวลาของคำถามและคำตอบ ด้วยเวอร์ชันที่ฉันกำลังทดสอบคุณจำเป็นต้องstring replace -r '(?s).\z' '' -- $t
(และฉันคาดหวังว่าพวกเขาต้องการเปลี่ยนสิ่งนั้นพวกเขาควรเปลี่ยนการตั้งค่าสถานะที่ส่งผ่านไปยัง PCRE) หรือที่ซับซ้อนกว่า มันเกี่ยวข้องกับตัวละครขึ้นบรรทัดใหม่ไม่ดีและฉันรู้ว่าพวกเขากำลังวางแผนที่จะเปลี่ยนแปลงสิ่งนั้นเช่นกัน
คำตอบที่พกพาได้ง่ายที่สุดและสั้นที่สุดคือ:
${t%?}
ใช้งานได้กับ bash, sh, ash, dash, busybox / ash, zsh, ksh ฯลฯ
มันทำงานได้โดยใช้การขยายพารามิเตอร์ของเชลล์วัยเรียน โดยเฉพาะการ%
ระบุที่จะลบคำต่อท้ายการจับคู่ที่เล็กที่สุดของพารามิเตอร์t
ที่ตรงกับรูปแบบ glob ?
(เช่น: ตัวละครใด ๆ )
ดู "ลบรูปแบบคำต่อท้ายที่เล็กที่สุด" ที่นี่สำหรับคำอธิบายโดยละเอียด (มาก) และพื้นหลังเพิ่มเติม ดูเอกสารสำหรับเชลล์ของคุณ (เช่น:) man bash
ใต้ "การขยายพารามิเตอร์"
หมายเหตุด้านข้างหากคุณต้องการลบอักขระตัวแรกแทนคุณจะต้องใช้${t#?}
เนื่องจากการ#
จับคู่จากด้านหน้าของสตริง (คำนำหน้า) แทนด้านหลัง (ส่วนต่อท้าย)
นอกจากนี้ยังมีข้อสังเกตว่าทั้งสอง%
และ#
มี%%
และ##
รุ่นซึ่งตรงกับเวอร์ชันที่ยาวที่สุดของรูปแบบที่กำหนดแทนที่จะสั้นที่สุด ทั้งสอง${t%%?}
และ${t##?}
จะทำเช่นเดียวกับตัวดำเนินการเดียวในกรณีนี้ (ดังนั้นอย่าเพิ่มอักขระพิเศษที่ไร้ประโยชน์) นี่เป็นเพราะ?
รูปแบบที่กำหนดให้ตรงกับอักขระเดียวเท่านั้น ผสมใน*
กับบางส่วนที่ไม่ใช่สัญลักษณ์และสิ่งที่ได้รับความน่าสนใจมากขึ้นด้วยและ%%
##
การทำความเข้าใจเกี่ยวกับการขยายพารามิเตอร์หรืออย่างน้อยก็รู้เกี่ยวกับการมีอยู่ของพวกเขาและรู้วิธีที่จะมองหามันมีประโยชน์อย่างเหลือเชื่อสำหรับการเขียนและถอดรหัสเชลล์สคริปต์ในหลาย ๆ รสชาติ การขยายพารามิเตอร์มักจะดูเหมือนว่าอาร์เคนเชลล์วูดูสำหรับหลาย ๆ คนเพราะ ... ดี ... พวกมันเป็นอาร์ซีเอของวูดู (แม้ว่าจะค่อนข้างดีถ้าคุณรู้ว่าจะมองหา แม้ว่าจะติดตั้งไว้ในเข็มขัดแล้วก็ดี
t=lkj
echo ${t:0:${#t}-1}
คุณได้รับสตริงย่อยจาก 0 ถึงความยาวสตริง -1 อย่างไรก็ตามโปรดทราบว่าการลบย่อยนี้เป็นการทุบตีที่เฉพาะเจาะจงและจะไม่ทำงานกับเชลล์อื่น ๆ
ตัวอย่างเช่นdash
ไม่สามารถแยกวิเคราะห์ได้
echo ${t:0:$(expr ${#t} - 1)}
ตัวอย่างเช่นบน Ubuntu /bin/sh
คือdash
คุณสามารถใช้head
เพื่อพิมพ์ทั้งหมดยกเว้นตัวอักษรตัวสุดท้าย
$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin
แต่น่าเสียดายที่บางรุ่นhead
ไม่มี-
ตัวเลือกนำหน้า นี่เป็นกรณีhead
ที่มาพร้อมกับ OS X
มันง่ายพอที่จะใช้นิพจน์ทั่วไป:
n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
การปรับแต่งบางอย่าง หากต้องการลบอักขระมากกว่าหนึ่งตัวคุณสามารถเพิ่มเครื่องหมายคำถามได้หลายเครื่องหมาย ตัวอย่างเช่นหากต้องการลบอักขระสองตัวสุดท้ายออกจากตัวแปร: $SRC_IP_MSG
คุณสามารถใช้:
SRC_IP_MSG=${SRC_IP_MSG%??}
เพียงเพื่อทำให้การใช้ bash บริสุทธิ์ที่เป็นไปได้สมบูรณ์:
#!/bin/bash
# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"
ไวยากรณ์แรกใช้สตริงย่อยจากสตริงไวยากรณ์คือ
สำหรับอันที่สองให้สังเกตเครื่องหมายซึ่งหมายความว่า 'จากจุดสิ้นสุดของบรรทัด' และไวยากรณ์คือ
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}
และนี่คือรูปแบบสั้น ๆ สองรูปแบบที่กล่าวมาข้างต้น
echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"
สังเกตที่นี่อีกครั้ง%
สัญญาณหมายถึง 'ลบ (นั่นคือแทนที่ด้วย' ') รูปแบบที่ตรงกันสั้นที่สุด (ที่นี่แทนด้วยช่องว่างที่หลบหนี' \ 'จากจุดสิ้นสุดของพารามิเตอร์ - นี่ชื่อSTR
ในขณะที่เรายังสามารถใช้phpในบรรทัดคำสั่งหรือสคริปต์เชลล์ บางครั้งมันมีประโยชน์สำหรับการแยกวิเคราะห์การผ่าตัด
php -r "echo substr('Hello', 0, -1);"
// Output hell
ด้วยท่อ:
echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell
ใน ksh:
echo ${ORACLE_SID/%?/}