ฉันจะกำหนดค่าที่แตกต่างให้กับตัวแปรได้อย่างไรโดยขึ้นอยู่กับตัวแปรอื่น?


20

ฉันจะทำให้เชลล์สคริปต์สั้นลงได้อย่างไร

CODE="A"

if test "$CODE" = "A"
then
 PN="com.tencent.ig"
elif test "$CODE" = "a"
 then
 PN="com.tencent.ig"
elif test "$CODE" = "B"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "b"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "C"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "c"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "D"
 then
 PN="com.rekoo.pubgm"
elif test "$CODE" = "d"
 then
 PN="com.rekoo.pubgm"
else
 echo -e "\a\t ERROR!"
 echo -e "\a\t CODE KOSONG"
 echo -e "\a\t MELAKUKAN EXIT OTOMATIS"
 exit
fi

2
ฉันคิดว่านี่เป็นbashรหัสหรือไม่ หรือคุณมีเปลือกอื่น ๆ ในใจ?
Freddy

3
ในอนาคตฉันขอแนะนำให้แทนที่ข้อมูลส่วนบุคคลเช่น URL และสิ่งอื่น ๆ ด้วยสิ่งทั่วไปเช่น "com.hello.world"
เทรเวอร์บอยด์สมิ ธ

1
@IISomeOneII คุณควรถาม CodeGolf.SE แทน: P
mackycheese21

3
@Trevor ผมอยากแนะนำให้example.org, example.netฯลฯ เป็นโดเมนเหล่านี้จะถูกสงวนไว้เฉพาะสำหรับวัตถุประสงค์นี้ใน RFC 2606 และจะไม่นำมาใช้สำหรับหน่วยงานที่แท้จริง
Toby Speight

2
@TrevorBoydSmith Seconding Toby แนะนำ com.example ฯลฯ เนื่องจาก "hello.com" เป็นของ Google
David Conrad

คำตอบ:


61

ใช้caseคำสั่ง (แบบพกพาทำงานได้ในshเปลือกที่มีลักษณะเหมือนใด ๆ):

case "$CODE" in
    [aA] ) PN="com.tencent.ig" ;;
    [bB] ) PN="com.vng.pubgmobile" ;;
    [cC] ) PN="com.pubg.krmobile" ;;
    [dD] ) PN="com.rekoo.pubgm" ;;
    * ) printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
        exit 1 ;;
esac

ฉันขอแนะนำให้คุณเปลี่ยนชื่อตัวแปรของคุณจากตัวพิมพ์ใหญ่ (เช่นCODE) เป็นตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่(เช่นcodeหรือCode) มีชื่อตัวพิมพ์ใหญ่หลายตัวที่มีความหมายพิเศษและการใช้ชื่อซ้ำอีกครั้งโดยไม่ตั้งใจอาจทำให้เกิดปัญหาได้

หมายเหตุอื่น ๆ : การประชุมมาตรฐานคือการส่งข้อความข้อผิดพลาดไปที่ "ข้อผิดพลาดมาตรฐาน" มากกว่า "เอาท์พุทมาตรฐาน"; การ>&2เปลี่ยนเส้นทางทำเช่นนี้ นอกจากนี้หากสคริปต์ (หรือโปรแกรม) ล้มเหลววิธีที่ดีที่สุดคือออกจากสถานะที่ไม่ใช่ศูนย์ ( exit 1) ดังนั้นบริบทการโทรใด ๆ สามารถบอกได้ว่าเกิดอะไรขึ้น นอกจากนี้ยังเป็นไปได้ที่จะใช้สถานะที่แตกต่างเพื่อระบุปัญหาที่แตกต่างกัน (ดูที่ส่วน "รหัสออก" ของหน้าคู่มือcurlเพื่อเป็นตัวอย่างที่ดี) (ขอเครดิตกับStéphane Chazelas และ Monty Harder สำหรับคำแนะนำที่นี่)

ฉันแนะนำprintfแทนecho -e(และecho -n) เพราะมันพกพาได้มากกว่าระหว่าง OSes, รุ่น, การตั้งค่าและอื่น ๆ ฉันเคยเขียนสคริปต์หลายครั้งเนื่องจากการอัปเดตระบบปฏิบัติการรวมเวอร์ชันของ bash ที่คอมไพล์ด้วยตัวเลือกต่าง ๆ ซึ่งเปลี่ยนวิธีการechoทำงาน

$CODEไม่จำเป็นต้องใช้เครื่องหมายคำพูดคู่รอบๆ ที่นี่ สตริงใน a caseเป็นหนึ่งในบริบทไม่กี่แห่งที่ปลอดภัยที่จะทิ้งไว้ อย่างไรก็ตามฉันต้องการอ้างอิงตัวแปรอ้างอิงสองครั้งยกเว้นว่ามีเหตุผลเฉพาะที่ไม่ควรทำเพราะมันยากที่จะติดตามว่ามันปลอดภัยที่ไหนและไม่อยู่ที่ใดดังนั้นจึงปลอดภัยกว่าที่จะพูดให้เป็นสองเท่า


5
@IISomeOneII ที่จะนับเป็น*(และพิมพ์ข้อผิดพลาด) - รูปแบบ[aA]ตรงกันทั้ง "a" หรือ "A" แต่ไม่ใช่ทั้งสองอย่างพร้อมกัน
Gordon Davisson

6
นี่เป็นวิธีที่ถูกต้องในการทำเช่นนั้นตรงไปที่ตัวแทนในตอนท้ายเปลี่ยนเส้นทางไปยัง stderr และสร้างค่าทางออกที่ไม่เป็นศูนย์ สิ่งเดียวที่อาจจำเป็นต้องเปลี่ยนคือค่าทางออกเนื่องจากอาจมีข้อผิดพลาดมากกว่าหนึ่งข้อที่ต้องส่งคืน ในสคริปต์ที่ใหญ่กว่าอาจมีส่วน (อาจมาจากไฟล์อื่น) ที่กำหนด vales ทางออกreadonly Exit_BadCode=1เพื่อให้สามารถพูดexit $Exit_BadCodeแทน
Monty Harder

2
ถ้าไปกับทุบตีที่ผ่านมาจากนั้นใช้case "${CODE,}" inเพื่อให้แต่ละเงื่อนไขจะกลายเป็นเพียงแค่a), b)ฯลฯ
สตีฟ

2
@MontyHarder มันขึ้นอยู่กับ หากมีรหัสเหล่านี้สองสามร้อยรหัสแต่ละอันสอดคล้องกับสตริงดังนั้นแนวทางอื่นอาจดีกว่า สำหรับปัญหาที่แน่นอนนี้เพียงพอแล้ว
Kusalananda

2
@MontyHarder ขออภัยฉันควรชัดเจนกว่านี้ โดย "รหัส" $CODEฉันหมายถึง ฉันมักจะเรียกว่า "สถานะการออก" เสมอว่าอย่าเพิ่ง "รหัส" หากสคริปต์ต้องการใช้หลายร้อยปุ่มเพื่ออ้างถึงสตริงการใช้caseคำสั่งจะไม่สะดวก
Kusalananda

19

สมมติว่าคุณใช้รีbashลีส 4.0 หรือใหม่กว่า ...

CODE=A

declare -A domain

domain=(
   [a]=com.tencent.ig
   [b]=com.vng.pubgmobile
   [c]=com.pubg.krmobile
   [d]=com.rekoo.pubgm
)

PN=${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

ในรหัสฉันกำหนดอาเรย์แบบเชื่อมโยงที่มีชื่อโดเมนทั้งหมดโดยแต่ละอันจะเชื่อมโยงกับคีย์ตัวพิมพ์เล็ก

$PNตัวแปรมีการกำหนดชื่อโดเมนที่สอดคล้องกับต่ำดาด$CODEค่า ( ${CODE,,}ผลตอบแทนที่คุ้มค่าของการ$CODEกลายเป็นตัวอักษรกรณีที่ต่ำกว่าเท่านั้น) จากแถวนี้ แต่ถ้า$CODEไม่ตรงกับรายการที่ถูกต้องในdomainรายชื่อออกจากสคริปต์ด้วย ความผิดพลาด

การ${variable:?error message}แทนที่พารามิเตอร์จะขยายเป็นค่าของ$variable(โดเมนที่เหมาะสมในรหัส) แต่จะออกจากสคริปต์ด้วยข้อความแสดงข้อผิดพลาดหากค่านั้นว่างเปล่า คุณไม่ได้รับการจัดรูปแบบของข้อความแสดงข้อผิดพลาดเหมือนกับในรหัสของคุณ แต่โดยพื้นฐานแล้วจะทำงานเหมือนกันหาก$CODEไม่ถูกต้อง:

$ bash script.sh
script.sh: line 12: domain[${CODE,,}]: ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS

หากคุณใส่ใจเกี่ยวกับการนับตัวละครเราสามารถย่อให้สั้นลงได้อีก:

CODE=A
declare -A domain=( [a]=tencent.ig [b]=vng.pubgmobile [c]=pubg.krmobile [d]=rekoo.pubgm )
PN=com.${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

นอกเหนือจากการลบบรรทัดใหม่ที่ไม่จำเป็นฉันยังลบcom.จากแต่ละโดเมน (ซึ่งจะถูกเพิ่มในการมอบหมายให้แทนPN)

โปรดทราบว่ารหัสทั้งหมดข้างต้นจะสามารถใช้งานได้แม้จะมีค่าหลายตัวอักษรใน$CODE(หากมีรหัสที่ต่ำกว่าสำหรับรหัสเหล่านี้ในdomainอาร์เรย์)


หาก$CODEเป็นดัชนีตัวเลข (อิงเป็นศูนย์) แทนสิ่งนี้จะทำให้โค้ดง่ายขึ้นเล็กน้อย:

CODE=0

domain=( com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm )
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

สิ่งนี้จะทำให้ง่ายต่อการอ่านdomainอาร์เรย์จากไฟล์เสริมที่มีหนึ่งรายการต่อบรรทัด:

CODE=0

readarray -t domain <domains.txt
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

1
@IISomeOneII declare -A domainเพิ่งบอกว่าdomainควรเป็นตัวแปรอาเรย์ ("แฮ")
Kusalananda

1
@Isaac ตอนนี้แตกต่างจากคุณมากขึ้น ขอบคุณสำหรับหัวขึ้น.
Kusalananda

1
น่าจะดีกว่าถ้าใช้ zsh หรือ ksh93 $CODEสำหรับทุบตีคุณจะต้องเป็นรุ่นที่ผ่านมาและมันจะล้มเหลวสำหรับค่าที่ว่างเปล่าของ
Stéphane Chazelas

1
@ StéphaneChazelasใช่คุณจะได้รับข้อความแสดงข้อผิดพลาดเพิ่มเติมหนึ่งรายการเกี่ยวกับตัวห้อยอาร์เรย์ที่ไม่ถูกต้องถ้าไม่$CODEได้ตั้งค่าหรือว่างเปล่า แต่จะยังคงสร้างข้อความแสดงข้อผิดพลาดที่กำหนดเองที่ถูกต้องหลังจากนั้น
Kusalananda

1
@Kusalananda สคริปต์ใหม่ (POSIX ที่ถูกต้อง) โพสต์ ไม่มีข้อผิดพลาดการตรวจสอบสั้นมาก
ไอแซค

11

หากเชลล์ของคุณอนุญาตอาร์เรย์คำตอบที่สั้นที่สุดควรเป็นแบบนี้ใน bash:

declare -A site
site=( [a]=com.tencent.ig [b]=com.vng.pubgmobile [c]=com.pubg.krmobile [d]=com.rekoo.pubgm )

pn=${site[${code,}]}

นั่นคือสมมุติว่า$codeสามารถเป็น a, b, c หรือ d
ถ้าไม่เพิ่มการทดสอบเช่น:

case ${site,} in
    a|b|c|d)        pn=${site[${code,}]};;
    *)              pn="default site"
                    printf '\a\t %s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS'
                    exit 1
                    ;;
esac

หากอินพุตเป็น A มันจะทำงานกับสคริปต์นั้นหรือไม่ ขออภัยภาษาอังกฤษของฉันไม่ดี
IISomeOneII

2
ใช่การขยายตัวแปลงเป็นตัวพิมพ์เล็กตัวอักษรตัวแรกของ${var,} ${var}@IISomeOneII
Isaac

1
${var,}ดูเหมือนว่าจะเฉพาะเจาะจงทุบตี ฉันคิดว่ากลุ่มการเชื่อมโยงจะทำงานใน ksh และ zsh ด้วย
ilkkachu

@ilkkachu ใช่ถูกต้องในทั้งสองจำนวน
ไอแซค

ขอบคุณทุกคนผู้คนมากมายที่นี่ IIS
IISomeOneII

3

ฉันจะตอบคำถามนี้ในทิศทางที่ต่างออกไป แทนที่จะเขียนโค้ดข้อมูลของคุณลงในสคริปต์ใส่ข้อมูลนั้นลงในไฟล์ข้อมูลแยกต่างหากจากนั้นใช้รหัสเพื่อค้นหาไฟล์:

$ cat names.cfg 
a com.tencent.ig
b com.vng.pubgmobile
c com.pubg.krmobile
d com.rekoo.pubgm

$ cat lookup.sh
PN=$(awk -v code="${1:-}" 'tolower($1) == tolower(code) { print $2; }' names.cfg)
if [ -z "${PN}" ]; then
  printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
  exit 1
fi
echo "${PN}"

$ bash lookup.sh A
com.tencent.ig
$ bash lookup.sh a
com.tencent.ig
$ bash lookup.sh x
    ERROR!
    CODE KOSONG
    MELAKUKAN EXIT OTOMATIS

การแยกข้อกังวลเหล่านี้มีประโยชน์เล็กน้อย:

  • เพิ่มและลบข้อมูลอย่างง่ายดายและง่ายดายโดยไม่ต้องแก้ไขตรรกะของโค้ด
  • โปรแกรมอื่น ๆ สามารถนำข้อมูลกลับมาใช้ใหม่ได้เช่นนับจำนวนการจับคู่ที่อยู่ในโดเมนย่อยหนึ่ง ๆ
  • หากคุณมีขนาดใหญ่รายการของข้อมูลคุณสามารถจัดเรียงไว้บนดิสก์และการใช้lookการค้นหาแบบไบนารีได้อย่างมีประสิทธิภาพมัน (มากกว่าบรรทัดโดยบรรทัดgrepหรือawk)

1
หากคุณไปทางนี้คุณยังต้องจัดPNให้มีการตั้งค่าที่ถูกต้อง
ilkkachu

1
@ilkkachu จุดยุติธรรม ฉันพลาดสิ่งนั้นใน OP การแก้ไข
บิชอป

2
+1 สำหรับการแยกข้อมูลจากรหัส
ARP

1

คุณกำลังใช้ตัวอักษรเพื่อจัดทำดัชนีค่าหากคุณต้องใช้ตัวเลขมันจะกลายเป็นเรื่องง่ายเหมือน:

code=1
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

eval pn\=\${"$code"}

นั่นเป็นรหัสเชลล์แบบพกพาจะใช้กับเชลล์ส่วนใหญ่ได้
สำหรับทุบตีคุณอาจใช้: pn=${!code}หรือทุบตี / ksh / zsh pn=${@:code:1}ใช้งาน:

ตัวอักษร

หากคุณต้องใช้ตัวอักษรผู้ใช้ (จาก a ถึง z หรือ A ถึง Z) จะต้องถูกแปลงเป็นดัชนี:

code=a                              # or A, B, C, ... etc.
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code")|32)-96  ))}\"

ในรหัสอีกต่อไปเพื่อชี้แจงความตั้งใจและความหมายของแต่ละส่วน:

code=A

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

asciival=$(( $(printf '%d' "'$code") ))      # byte value of the ASCII letter.
upperval=$(( asciival |  32 ))               # shift to uppercase.
indexval=$(( upperval -  96 ))               # convert to an index from a=1.
eval arg\=\"\$\{$indexval\}\"                # the argument at such index.

หากคุณต้องการแปลงเป็นค่าที่ต่ำกว่าให้ใช้: $(( asciival & ~32 ))(ตรวจสอบให้แน่ใจว่าบิต 6 ของค่า ascii ไม่มีการตั้งค่าไว้)

รหัสข้อผิดพลาด

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

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

จากนั้นเรียกใช้ฟังก์ชันนั้นพร้อมข้อความเฉพาะที่คุณต้องการ

errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS"

โปรดทราบว่าค่าการออกผลลัพธ์ที่ได้รับจากexitcode(ตัวอย่างที่นี่คือ 27)

สคริปต์เต็มรูปแบบ (ด้วยการตรวจสอบข้อผิดพลาด) จะกลายเป็น:

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

code=${1:-A}

case "$code" in 
    [a-d]|[A-D]) : ;;
    *)           errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS" ;;
esac

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code") & ~32) - 64  ))}\"

printf 'Code=%s Argument=%s\n' "$code" "$pn"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.