แยกสตริงบนโคลอนใน / bin / sh


9

dashสคริปต์ของฉันใช้พารามิเตอร์ในรูปแบบของhostname:portคือ:

myhost:1234

ในขณะที่พอร์ตเป็นทางเลือกเช่น:

myhost

ฉันต้องอ่านโฮสต์และพอร์ตเป็นตัวแปรแยกต่างหาก ในกรณีแรกฉันทำได้:

HOST=${1%%:*}
PORT=${1##*:}

แต่นั่นจะไม่ทำงานในกรณีที่สองเมื่อพอร์ตถูกละเว้น echo ${1##*:}เพียงแค่ส่งคืนชื่อโฮสต์แทนที่จะเป็นสตริงว่าง

ใน Bash ฉันสามารถทำได้:

IFS=: read A B <<< asdf:111

dashแต่นั่นไม่ได้ทำงานใน

ฉันสามารถแยกสตริงบน:ในประโดยไม่ต้องเรียกใช้โปรแกรมภายนอก ( awk, trฯลฯ )?


4
ตรวจสอบให้แน่ใจว่าได้ทำการแยกบนโคลอนสุดท้ายหากคุณต้องการรองรับ IPv6 และอย่าแยกโคลอนภายในวงเล็บเหลี่ยม
Ferrybig

@Ferrybig %%ทำให้มันโลภ (เมื่อเทียบกับ%) ดังนั้นจึงทำเช่นนี้อย่างน้อยก็ในบางส่วน ##มันจะไม่ทำงานร่วมกับ
jpaugh

คำตอบ:


18

แค่ทำ:

case $1 in
  (*:*) host=${1%:*} port=${1##*:};;
  (*)   host=$1      port=$default_port;;
esac

คุณอาจต้องการที่จะเปลี่ยนcase $1ไปcase ${1##*[]]}ยังบัญชีสำหรับค่า$1เช่น[::1](ที่อยู่ IPv6 โดยไม่ต้องพอร์ตบางส่วน)

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

set -o noglob # disable glob part
IFS=:         # split on colon
set -- $1     # split+glob

host=$1 port=${2:-$default_port}

(แม้ว่าจะไม่อนุญาตชื่อโฮสต์ที่มีเครื่องหมายโคลอน (เช่นสำหรับที่อยู่ IPv6 ข้างต้น))

ตัวดำเนินการแยก + glob นั้นขวางทางและก่อให้เกิดอันตรายมากในช่วงเวลาที่เหลือซึ่งมันดูเหมือนว่าจะใช้ได้เฉพาะเมื่อใดก็ตามที่ต้องการ (แม้ว่าฉันจะเห็นด้วยว่ามันยุ่งยากมากที่จะใช้โดยเฉพาะอย่างยิ่งเมื่อพิจารณาว่า POSIX shไม่มี สนับสนุนขอบเขตท้องถิ่นค่าสำหรับตัวแปร ( $IFSที่นี่) หรือสำหรับตัวเลือก ( noglobที่นี่) (แม้ว่าashและอนุพันธ์ชอบdashคือบางส่วนของคนที่ทำ (ร่วมกับ AT & T ใช้งานของksh, zshและbash4.4 และสูงกว่า))

โปรดทราบว่าIFS=: read A B <<< "$1"มีปัญหาของตัวเองไม่กี่:

  • คุณลืม-rเครื่องหมายแบ็กสแลชซึ่งหมายถึงกระบวนการพิเศษบางอย่าง
  • มันจะแยก[::1]:443ออกเป็น[และ:1]:443แทน[และสตริงที่ว่างเปล่า (ซึ่งคุณจะต้องIFS=: read -r A B rest_ignoredหรือ[::1]และ443(ซึ่งคุณไม่สามารถใช้วิธีการที่)
  • มันตัดทุกอย่างที่เกิดขึ้นครั้งแรกของอักขระขึ้นบรรทัดใหม่ดังนั้นจึงไม่สามารถใช้กับสตริงที่กำหนดเองได้ (เว้นแต่คุณจะใช้-d ''ในzshหรือbashและข้อมูลไม่มีอักขระ NUL แต่จากนั้นให้สังเกตว่า herestrings (หรือ heredocs) เพิ่ม อักขระขึ้นบรรทัดใหม่พิเศษ!)
  • ในzsh(โดยที่ไวยากรณ์มาจาก) และbashที่นี่มีการใช้งานสตริงโดยใช้ไฟล์ชั่วคราวดังนั้นโดยทั่วไปจะมีประสิทธิภาพน้อยกว่าการใช้${x#y}หรือแยกตัวดำเนินการ + glob

7
ในปี 2561 ตามมติปีใหม่เราทุกคนควรหยุดเขียนสคริปต์ที่จะหยุดด้วย IPv6
Philippos

@Phippippos สายเกินไปสองสัปดาห์!
RonJohn

@ RonJohn: สายเกินไปสองทศวรรษแล้ว
Philippos

6

เพียงลบ:คำสั่งที่แยกต่างหาก ยังลบ $ host จากอินพุตเพื่อรับพอร์ต:

host=${1%:*}
port=${1#"$host"}
port=${port#:}


1

สตริงที่นี่เป็นเพียงทางลัด syntactic สำหรับเอกสารบรรทัดเดียวที่นี่

$ set myhost:1234
$ IFS=: read A B <<EOF
> $1
> EOF
$ echo "$A"
myhost
$ echo "B"
1234
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.