สร้างดัชนีสตริงใน bash


15

ฉันจะอ้างถึงสตริงตามดัชนีใน sh / bash ได้อย่างไร? นั่นคือโดยทั่วไปมันแยก

ฉันพยายามตัดชื่อไฟล์ 5 ตัวอักษร ชื่อทั้งหมดมีโครงสร้าง: name_nr_code ฉันกำลังพยายามลบบิตรหัสตัวอักษรและตัวเลข 5 ตัว name_nr_มีความยาว 10 อักขระเสมอ

มีสิ่งที่ชอบ;

for i in * ; do mv "$i" "$i"[:10] ; done


5
ทำไมถึงต้องbashติดแท็กหากคุณกำลังขอทางshแก้ไข?
Stéphane Chazelas

คำตอบ:


15

เรียบง่ายเช่นนี้

(ทุบตี)

for i in * ; do mv -- "$i" "${i:0:5}" ; done

voila

และคำอธิบายจากคู่มือการใช้สคริปต์การทุบตีขั้นสูง ( บทที่ 10 การจัดการตัวแปร ) (พร้อมNOTEอินไลน์พิเศษเพื่อเน้นข้อผิดพลาดในคู่มือนั้น):

การสกัดซับสตริง

${string:position}

สารสกัดจาก substring ที่$string$position

ถ้า$stringพารามิเตอร์คือ "*" หรือ "@" $positionจากนั้นสารสกัดนี้พารามิเตอร์ตำแหน่งเริ่มต้นที่

${string:position:length}

สารสกัดจาก$lengthตัวละครของ substring จากที่$string$position

NOTEไม่มีเครื่องหมายคำพูดรอบการขยายพารามิเตอร์! echoไม่ควรใช้สำหรับข้อมูลโดยพลการ

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                       # abcABC123ABCabc
echo ${stringZ:1}                       # bcABC123ABCabc
echo ${stringZ:7}                       # 23ABCabc 

echo ${stringZ:7:3}                     # 23A
                                        # Three characters of substring.


# Is it possible to index from the right end of the string?

echo ${stringZ:-4}                      # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . . 

echo ${stringZ:(-4)}                    # Cabc
echo ${stringZ: -4}                     # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

ตำแหน่งและระยะเวลาในการขัดแย้งสามารถ "แปร" นั่นคือแสดงเป็นตัวแปรมากกว่าที่จะเป็นตัวเลขคงที่


ถ้า$stringพารามิเตอร์คือ "*" หรือ "@" จากนั้นสารสกัดนี้สูงสุดของพารามิเตอร์ตำแหน่งเริ่มต้นที่$length$position

echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.

NOTE: expr substrเป็นส่วนขยายของ GNU

expr substr $string $position $length

สารสกัดจาก$lengthตัวละครจากเริ่มต้นที่$string$position

stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`           # ab
echo `expr substr $stringZ 4 3`           # ABC

NOTE: นั่นechoซ้ำซ้อนและทำให้มันน่าเชื่อถือน้อยลง expr substr + "$string1" 1 2ใช้

NOTE: exprจะกลับมาพร้อมกับสถานะออกไม่เป็นศูนย์หากเอาต์พุตเป็น 0 (หรือ -0, 00 ... )


BTW abs-guideหนังสือที่มีอยู่ในพื้นที่เก็บข้อมูลอย่างเป็นทางการอูบุนตู


การพูดว่า "position" นั้นทำให้เข้าใจผิดเล็กน้อยเนื่องจากเป็น offset ซึ่งหมายความว่า${var:1}จะไม่ส่งคืนค่าvarจาก "ตำแหน่งที่ 1" แต่จริงๆแล้วมาจากอันดับที่ 2
Kusalananda

นั่นเป็นเรื่องจริง แต่ตราบใดที่คุณไม่เห็นด้วยอาจมีตำแหน่งที่เป็นศูนย์ ไหนดีกับฉัน

9

ใน POSIX sh,

  • "${var%?????}"ถูก$varถอดออกจากตัวอักษรต่อท้าย 5 ตัวสุดท้าย (หรือ$varหาก$varมีน้อยกว่า 5 ตัวอักษร)

  • "${var%"${var#??????????}"}"เป็น 10 $varตัวอักษรแรกของ

  • "${var%_*}"ถูก$varถอดออกจากสตริงที่สั้นที่สุดที่ตรงกับ_*ในตอนท้ายของ$var( foo_bar_baz-> foo_bar)
  • "${var%%_*}": การจับคู่แบบเดียวกัน แต่ยาวที่สุดแทนที่จะเป็นการจับคู่ที่สั้นที่สุด ( foo_bar_baz-> foo)
  • หากคุณต้องการได้รับfoo_bar_: "${var%"${var##*_}"}"( ${var##pattern}เหมือนกับ${var%%pattern}แต่มองหารูปแบบที่จุดเริ่มต้น$varแทนที่จะจบ)

ด้วยzsh:

  • $var[1,-6] สำหรับตัวละครแรกถึงอันดับที่ 6 จากตอนจบ (ดังนั้นทั้งหมดยกเว้น 5 ตัวสุดท้าย)
  • $var[1,10] สำหรับ 10 ตัวแรก

ด้วยksh, bashหรือzsh:

  • "${var:0:10}": 10 ตัวแรกของ $var

ด้วยbashหรือzsh:

  • "${var:0:-5}": ทั้งหมดยกเว้นอักขระ 5 ตัวสุดท้าย (ให้ข้อผิดพลาดและออกจากสคริปต์หาก$varตั้งค่าไว้ แต่มีน้อยกว่า 5 ตัวอักษรและเมื่อ$varไม่ได้ตั้งค่าด้วยzsh)

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

first_10=`expr " $var" : ' \(.{1,10\}\)'` # beware the exit status
                                          # may be non-zero if the
                                          # result is 0 or 0000000000

all_but_last_5=`expr " $var" : ' \(.*\).\{5\}'`

นอกจากนี้คุณยังมีขีดจำกัดความยาว$var(แตกต่างกันระหว่างระบบ)

ในโซลูชันเหล่านั้นทั้งหมดหาก$varมีไบต์ที่ไม่สามารถเป็นส่วนหนึ่งของอักขระที่ถูกต้องได้ YMMV


ของฉันพวกเขาเกิดขึ้นกับไวยากรณ์ที่น่าเกลียดบางอย่างสำหรับการจัดฟันภายใน
แมว

2

shไม่ได้มีวิธีในการดึงสตริงย่อยออกจากสตริง (เท่าที่ฉันเห็น) แต่bashคุณอาจทำได้

${i:0:10}

iนี้จะให้ตัวละครครั้งแรกเมื่อสิบของค่าของตัวแปร

${variable:offset:length}รูปแบบทั่วไปคือ


2

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

substr=${string:4:5} # start at position 4, length 5.

ในdashไม่รองรับการออฟเซ็ต แต่คุณสามารถใช้รูปแบบนำหน้าและต่อท้ายได้:

remove_first3=${string#???}
remove_last2=${string%??}

0

ก่อนอื่นอย่าใช้การforวนซ้ำสำหรับชื่อไฟล์

จากนั้นสิ่งนี้จะช่วยได้

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done

3
ทำไมการใช้forชื่อไฟล์ถึงไม่ดี?
choroba

อ้างถึงตัวแปรของคุณและใช้printfเพื่อความปลอดภัย ... read -rและ
Kusalananda

3
สหกรณ์ของห่วงดียกเว้นอาจจะหายไปfor --ฉันเห็นข้อบกพร่องอย่างน้อย 10 ข้อในโค้ด 4 บรรทัดของคุณ! หลายวิธีปฏิบัติที่ไม่ดีที่รู้จักกันดีเช่นสมมติว่าชื่อไฟล์เป็นบรรทัดเดียวใช้ echo, ไม่มีคำพูด
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.