จะเขียนเชลล์สคริปต์เพื่อกำหนดเกรดตัวอักษรให้กับช่วงตัวเลขได้อย่างไร


19

ฉันต้องการสร้างสคริปต์ที่จะแจ้งให้คุณทราบหมายเลขระหว่าง 0-100 แล้วให้คะแนนตามตัวเลข

ฉันต้องการมันในทุบตี

PS3='Please enter your choice: '
(Something here)

do
case $
    "0-59")
        echo "F"
        ;;
    "60-69")
        echo "D"
        ;;
    "70-79")
        echo "C"
        ;;
    "Quit")
        break
        ;;
    *) echo invalid option;;
esac
done

คำตอบ:


20

ความกะทัดรัดเทียบกับการอ่านได้: พื้นกลาง

ในขณะที่คุณได้เห็นปัญหานี้ยอมรับว่าการแก้ปัญหาที่มีในระดับปานกลางยาวและค่อนข้างซ้ำ แต่สามารถอ่านได้สูง ( terdon ของและAB ของคำตอบทุบตี) เช่นเดียวกับผู้ที่มีความสั้นมาก แต่ไม่ใช่งานง่ายและมากน้อยตนเอง documenting (ทิมหลามและทุบตีคำตอบและคำตอบของperlเกล็นแจ็คแมน) วิธีการทั้งหมดนี้มีค่า

นอกจากนี้คุณยังสามารถแก้ปัญหานี้ด้วยรหัสที่อยู่ตรงกลางระหว่างความกะทัดรัดและความสามารถในการอ่าน วิธีนี้เกือบจะอ่านได้เหมือนโซลูชันที่ยาวกว่าและมีความยาวใกล้กับโซลูชันลึกลับที่มีขนาดเล็ก

#!/usr/bin/env bash

read -erp 'Enter numeric grade (q to quit): '
case $REPLY in [qQ]) exit;; esac

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

for letter in F D C B A; do
    ((REPLY <= cutoffs[$letter])) && { echo $letter; exit; }
done
echo "Grade out of range."

ในโซลูชันทุบตีนี้ฉันได้รวมบรรทัดว่างไว้เพื่อปรับปรุงความสามารถในการอ่าน แต่คุณสามารถลบออกได้หากคุณต้องการให้สั้นลง

บรรทัดที่ว่างเปล่ารวมนี้เป็นจริงเพียงเล็กน้อยสั้นกว่าcompactified แตกต่างยังคงสามารถอ่านได้สวยของวิธีการแก้ปัญหาของทุบตี AB ข้อได้เปรียบหลักเหนือวิธีการคือ:

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

ข้อดีทั้งสามข้อนี้เกิดขึ้นเนื่องจากวิธีนี้ใช้อินพุตของผู้ใช้เป็นข้อมูลตัวเลขมากกว่าโดยการตรวจสอบตัวเลขส่วนประกอบด้วยตนเอง

มันทำงานอย่างไร

  1. อ่านอินพุตจากผู้ใช้ ปล่อยให้พวกเขาใช้ปุ่มลูกศรเพื่อเลื่อนไปมาในข้อความที่พวกเขาป้อน ( -e) และอย่าตีความ\ว่าเป็นอักขระยกเว้น ( -r)
    สคริปต์นี้ไม่ใช่โซลูชันที่มีคุณสมบัติหลากหลาย - ดูด้านล่างสำหรับการปรับแต่ง - แต่คุณสมบัติที่มีประโยชน์เหล่านั้นทำให้มีความยาวได้สองอักขระเท่านั้น ฉันแนะนำให้ใช้เสมอ-rกับreadเว้นแต่คุณจะรู้ว่าคุณต้องปล่อยให้ผู้ใช้\หลบหนี
  2. หากผู้ใช้เขียนqหรือQออกจาก
  3. สร้างอาเรย์แบบเชื่อมโยง ( ) เติมด้วยระดับตัวเลขสูงสุดที่เกี่ยวข้องกับแต่ละเกรดตัวอักษรdeclare -A
  4. วนรอบคะแนนตัวอักษรจากต่ำสุดไปหาสูงสุดตรวจสอบว่าหมายเลขที่ผู้ใช้ให้นั้นต่ำพอที่จะตกอยู่ในช่วงตัวเลขของแต่ละเกรดของตัวอักษร
    ด้วยการประเมินผลคณิตศาสตร์ชื่อตัวแปรไม่จำเป็นต้องขยายได้ด้วย(( )) $(ในสถานการณ์อื่น ๆ ส่วนใหญ่หากคุณต้องการใช้ค่าของตัวแปรแทนชื่อคุณต้องทำสิ่งนี้ )
  5. ถ้ามันอยู่ในช่วงที่พิมพ์เกรดและทางออก
    เพื่อความกระชับผมใช้ลัดวงจรและผู้ประกอบการ ( &&) มากกว่า-ifthen
  6. หากลูปเสร็จสิ้นและไม่ได้จับคู่ช่วงให้ถือว่าจำนวนที่ป้อนนั้นสูงเกินไป (มากกว่า 100) และบอกผู้ใช้ว่าอยู่นอกช่วง

วิธีนี้ทำงานด้วยการป้อนข้อมูลที่แปลก

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

  • ข้อมูลที่ไม่เหมือนกับตัวเลขจะถูกตีความว่าเป็น 0
  • ด้วยอินพุตที่ดูเหมือนหมายเลข (เช่นถ้าขึ้นต้นด้วยหลัก) แต่มีอักขระที่ไม่ถูกต้องสคริปต์จะปล่อยข้อผิดพลาด
  • การป้อนข้อมูล Multi-หลักเริ่มต้นด้วยการ0ถูกตีความว่าเป็นในฐานแปด ตัวอย่างเช่นสคริปต์จะบอกคุณ 77 คือ C ในขณะที่ 077 เป็น D แม้ว่าผู้ใช้บางรายอาจต้องการสิ่งนี้ส่วนใหญ่อาจไม่และอาจทำให้เกิดความสับสน
  • ในด้านบวกเมื่อได้รับนิพจน์ทางคณิตศาสตร์สคริปต์นี้จะลดความซับซ้อนของมันโดยอัตโนมัติและกำหนดเกรดตัวอักษรที่เกี่ยวข้อง ตัวอย่างเช่นมันจะบอกคุณ 320/4 เป็น B

เวอร์ชันที่ขยายและโดดเด่นอย่างสมบูรณ์

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

#!/usr/bin/env bash
shopt -s extglob

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

while read -erp 'Enter numeric grade (q to quit): '; do
    case $REPLY in  # allow leading/trailing spaces, but not octal (e.g. "03") 
        *( )@([1-9]*([0-9])|+(0))*( )) ;;
        *( )[qQ]?([uU][iI][tT])*( )) exit;;
        *) echo "I don't understand that number."; continue;;
    esac

    for letter in F D C B A; do
        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
    done
    echo "Grade out of range."
done

นี่ยังเป็นโซลูชันที่กะทัดรัด

คุณลักษณะนี้เพิ่มอะไรบ้าง

จุดสำคัญของสคริปต์ที่ขยายนี้คือ:

  • การตรวจสอบอินพุต สคริปต์ของ terdon ตรวจสอบอินพุตด้วยดังนั้นฉันจึงแสดงอีกวิธีหนึ่งซึ่งเสียสละบางอย่างสั้น ๆ แต่มีประสิทธิภาพมากขึ้นทำให้ผู้ใช้ป้อนช่องว่างนำหน้าและต่อท้ายและปฏิเสธที่จะอนุญาตให้มีการแสดงออกที่อาจหรือไม่ตั้งใจให้เป็นฐานแปด (เว้นแต่เป็นศูนย์) .if [[ ! $response =~ ^[0-9]*$ ]] ...
  • ฉันใช้caseกับการขยายแบบวงกลมแทนการ[[ใช้ตัวดำเนินการ=~ จับคู่นิพจน์ทั่วไป (ตามคำตอบของ terdon ) ฉันทำอย่างนั้นเพื่อแสดงว่า (และวิธี) มันก็สามารถทำได้เช่นกัน Globs และ regexps เป็นสองวิธีในการระบุรูปแบบที่ตรงกับข้อความและทั้งสองวิธีนี้ใช้ได้สำหรับแอปพลิเคชันนี้
  • เช่นเดียวกับสคริปต์ทุบตีของ ABฉันได้ล้อมรอบทั้งหมดไว้ในลูปด้านนอก (ยกเว้นการสร้างcutoffsอาร์เรย์เริ่มต้น) มันขอหมายเลขและให้คะแนนตัวอักษรที่เกี่ยวข้องตราบใดที่อินพุตเทอร์มินัลพร้อมใช้งานและผู้ใช้ไม่ได้บอกให้เลิก ตัดสินโดยdo... doneรอบรหัสในคำถามของคุณดูเหมือนว่าคุณต้องการ
  • เพื่อให้เลิกง่ายฉันยอมรับตัวแปรที่ไม่ต้องตรงตามตัวพิมพ์เล็กqและquitใหญ่

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

คำอธิบาย: การใช้ continue

เมื่อฉันต้องการข้ามส่วนที่เหลือของร่างกายด้านนอก whileลูปฉันใช้continueคำสั่ง สิ่งนี้จะนำมันกลับขึ้นไปด้านบนของลูปเพื่ออ่านอินพุตเพิ่มเติมและรันการวนซ้ำอีกครั้ง

ครั้งแรกที่ฉันทำสิ่งนี้เฉพาะลูปที่ฉันอยู่คือwhileลูปภายนอกดังนั้นฉันจึงสามารถโทรได้continueโดยไม่มีการโต้แย้ง (ฉันอยู่ในcaseโครงสร้าง แต่ไม่ส่งผลกระทบต่อการทำงานของbreakหรือcontinue )

        *) echo "I don't understand that number."; continue;;

อย่างไรก็ตามครั้งที่สองฉันอยู่ในforวงด้านในซึ่งตัวมันเองซ้อนอยู่ภายในwhileวงด้านนอก ถ้าฉันใช้continueโดยไม่มีข้อโต้แย้งสิ่งนี้จะเทียบเท่าcontinue 1และจะดำเนินการต่อforวงในแทนwhileวงด้านนอก

        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }

ดังนั้นในกรณีนี้ฉันใช้continue 2เพื่อค้นหา bash และวนลูปที่สองต่อไปแทน

คำอธิบาย: caseป้ายกำกับที่มี Globs

ฉันไม่ได้ใช้caseเพื่อหาตัวเลขตัวอักษรเกรดถังขยะที่ตกอยู่ใน (เช่นในคำตอบ bash ของ AB ) แต่ฉันจะใช้caseเพื่อตัดสินใจว่าควรพิจารณาการป้อนข้อมูลของผู้ใช้หรือไม่:

  • หมายเลขที่ถูกต้อง *( )@([1-9]*([0-9])|+(0))*( )
  • คำสั่งเลิก *( )[qQ]?([uU][iI][tT])*( )
  • สิ่งอื่นใด (และการป้อนข้อมูลที่ไม่ถูกต้อง) *

เหล่านี้เป็นglobs เปลือก

  • แต่ละคนตามมาด้วยสิ่ง)ที่ไม่ตรงกันโดยการเปิดใด ๆ(ซึ่งก็คือcaseไวยากรณ์ของการแยกรูปแบบจากคำสั่งที่ทำงานเมื่อมีการจับคู่
  • ;;เป็นcaseไวยากรณ์ของการระบุจุดสิ้นสุดของคำสั่งที่จะเรียกใช้สำหรับการจับคู่กรณี paticular (และไม่ควรทดสอบกรณีที่ตามมาหลังจากเรียกใช้)

เชลล์แบบปกติจะจัดให้มี*อักขระเท่ากับศูนย์หรือมากกว่า?เพื่อจับคู่อักขระหนึ่งตัวและคลาส / ช่วงอักขระใน[ ]วงเล็บ แต่ฉันกำลังใช้การขยายแบบวงกลมซึ่งไปไกลกว่านั้น การขยายแบบวงกลมจะถูกเปิดใช้งานตามค่าเริ่มต้นเมื่อใช้bashแบบโต้ตอบ แต่จะถูกปิดใช้งานตามค่าเริ่มต้นเมื่อเรียกใช้สคริปต์ shopt -s extglobคำสั่งที่ด้านบนของสคริปต์เปลี่ยนมันใน

คำอธิบาย: ขยาย Globbing

*( )@([1-9]*([0-9])|+(0))*( )ซึ่งจะตรวจสอบอินพุตตัวเลขตรงกับลำดับของ:

  • เป็นศูนย์หรือมากกว่าช่องว่าง ( *( )) *( )การแข่งขันสร้างศูนย์หรือมากกว่าของรูปแบบในวงเล็บซึ่งนี่เป็นเพียงพื้นที่
    จริง ๆ แล้วมีช่องว่างแนวนอนช่องว่างและแท็บสองประเภทและบ่อยครั้งที่ต้องการจับคู่แท็บด้วยเช่นกัน แต่ฉันไม่ต้องกังวลเกี่ยวกับเรื่องนี้ที่นี่เพราะสคริปต์นี้เขียนขึ้นสำหรับการป้อนข้อมูลด้วยตนเองและการ-eตั้งค่าสถานะเพื่อreadเปิดใช้งาน GNU readline นี่คือเพื่อให้ผู้ใช้สามารถย้ายไปมาในข้อความของพวกเขาด้วยปุ่มลูกศรซ้ายและขวา แต่มันมีผลข้างเคียงของการป้องกันโดยทั่วไปแท็บจากการป้อนอย่างแท้จริง
  • หนึ่งเหตุการณ์ ( @( )) ของทั้ง ( |):
    • ตัวเลขที่ไม่ใช่ศูนย์ ( [1-9]) ตามด้วยศูนย์หรือมากกว่า (*( ) ) ของหลักใด ๆ ( [0-9])
    • หนึ่งหรือมากกว่า ( +( )) 0ของ
  • เป็นศูนย์หรือมากกว่าเว้นวรรค ( *( )) อีกครั้ง

*( )[qQ]?([uU][iI][tT])*( )ซึ่งตรวจสอบคำสั่ง quitตรงกับลำดับของ:

  • เป็นศูนย์หรือมากกว่าช่องว่าง ( *( ))
  • qหรือQ( [qQ])
  • เลือกได้ - เช่นศูนย์หรือหนึ่งเหตุการณ์ ( ?( )) - จาก:
    • uหรือU( [uU]) ตามด้วยiหรือI( [iI]) ตามด้วยtหรือT( [tT])
  • เป็นศูนย์หรือมากกว่าเว้นวรรค ( *( )) อีกครั้ง

Variant: การตรวจสอบความถูกต้องของอินพุตด้วยนิพจน์ปกติเพิ่มเติม

หากคุณต้องการที่จะทดสอบการป้อนข้อมูลของผู้ใช้กับการแสดงออกปกติมากกว่า shell glob คุณอาจต้องการใช้รุ่นนี้ซึ่งใช้งานได้เหมือนกัน แต่ใช้[[และ=~(เช่นในคำตอบของ Terdon ) แทนcaseและขยายกลม

#!/usr/bin/env bash
shopt -s nocasematch

declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100

while read -erp 'Enter numeric grade (q to quit): '; do
    # allow leading/trailing spaces, but not octal (e.g., "03")
    if [[ ! $REPLY =~ ^\ *([1-9][0-9]*|0+)\ *$ ]]; then
        [[ $REPLY =~ ^\ *q(uit)?\ *$ ]] && exit
        echo "I don't understand that number."; continue
    fi

    for letter in F D C B A; do
        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
    done
    echo "Grade out of range."
done

ข้อดีที่เป็นไปได้ของวิธีนี้คือ:

  • ในกรณีพิเศษนี้ไวยากรณ์นั้นง่ายกว่าเล็กน้อยอย่างน้อยก็ในรูปแบบที่สองซึ่งฉันจะตรวจสอบคำสั่ง quit นี่เป็นเพราะฉันสามารถตั้งค่าnocasematchตัวเลือกของเชลล์แล้วทุกกรณีของqและquitครอบคลุมโดยอัตโนมัติ

    นั่นคือสิ่งที่shopt -s nocasematchคำสั่งทำ shopt -s extglobคำสั่งที่ถูกละไว้เป็น globbing ไม่ได้ใช้ในรุ่นนี้

  • ทักษะการแสดงออกปกติเป็นเรื่องธรรมดามากกว่าความสามารถใน extglobs ของ bash

คำอธิบาย: นิพจน์ปกติ

สำหรับรูปแบบที่ระบุทางด้านขวาของ=~โอเปอเรเตอร์นี่คือวิธีการที่นิพจน์ทั่วไปทำงาน

^\ *([1-9][0-9]*|0+)\ *$ซึ่งจะตรวจสอบอินพุตตัวเลขตรงกับลำดับของ:

  • จุดเริ่มต้น - คือขอบซ้าย - ของบรรทัด ( ^)
  • *ช่องว่างเป็นศูนย์หรือมากกว่า ( , postfix ที่ใช้) ตามปกติแล้วพื้นที่ไม่จำเป็นต้องมีการ\หลีกเลี่ยงในนิพจน์ปกติ แต่จำเป็นต้องมี[[เพื่อป้องกันข้อผิดพลาดทางไวยากรณ์
  • สตริงย่อย ( ( )) ที่เป็นหนึ่งหรืออื่น ๆ ( |) ของ:
    • [1-9][0-9]*: ตัวเลขที่ไม่ใช่ศูนย์ ( [1-9]) ตามด้วยศูนย์หรือมากกว่า ( *, postfix ที่ใช้) ของตัวเลขใด ๆ ( [0-9])
    • 0+หนึ่งหรือมากกว่า ( +, postfix ประยุกต์) 0ของ
  • ช่องว่างเป็นศูนย์หรือมากกว่า (\ * ) เช่นก่อน
  • จุดสิ้นสุด - เช่น, ขอบด้านขวา - ของเส้น ( $)

ต่างจากcaseป้ายกำกับที่ตรงกับนิพจน์ทั้งหมดที่ถูกทดสอบ=~ส่งคืนค่าจริงหากส่วนใดส่วนหนึ่งของนิพจน์ทางซ้ายตรงกับรูปแบบที่กำหนดให้เป็นนิพจน์ทางขวา นี่คือสาเหตุที่^และ$anchors ระบุจุดเริ่มต้นและจุดสิ้นสุดของบรรทัดที่นี่และไม่สอดคล้อง syntactically กับสิ่งที่ปรากฏในวิธีการที่มีcaseและ extglobs

วงเล็บที่มีความจำเป็นเพื่อให้^และ$ผูกไว้กับความร้าวฉานของและ[1-9][0-9]* 0+มิฉะนั้นจะเป็นการแยก^[1-9][0-9]*และ0+$และจับคู่อินพุตใด ๆ ที่เริ่มต้นด้วยตัวเลขที่ไม่ใช่ศูนย์หรือลงท้ายด้วย0(หรือทั้งสองอย่างซึ่งอาจยังคงมีตัวเลขที่ไม่ใช่ตัวเลขอยู่)

^\ *q(uit)?\ *$ซึ่งตรวจสอบคำสั่ง quitตรงกับลำดับของ:

  • จุดเริ่มต้นของบรรทัด (^ )
  • ช่องว่างเป็นศูนย์หรือมากกว่า (\ *ดูคำอธิบายข้างต้น)
  • qตัวอักษร หรือQตั้งแต่shopt nocasematchเปิดใช้งาน
  • เลือกได้ - เช่นศูนย์หรือหนึ่งเหตุการณ์ (postfix ?) - ของสตริงย่อย ( ( )):
    • uตามด้วยตามด้วยi tหรือเนื่องจากshopt nocasematchเปิดใช้งานuอาจเป็นU; อิสระiอาจจะI; และเป็นอิสระ, อาจจะt T(นั่นคือความเป็นไปได้ไม่จำกัดuitและUIT.)
  • ศูนย์หรือมากกว่าช่องว่างอีกครั้ง ( \ *)
  • จุดสิ้นสุดของบรรทัด ( $)

3
บอกความจริงกับฉัน .. ใช้เวลานานเท่าไหร่ ;)
heemayl

4
@ heemayl ฉันไม่แน่ใจอย่างเต็มที่เพราะฉันเขียนมันในส่วนเล็ก ๆ มากมายตลอดทั้งวัน (ตามด้วยการอ่านและการแก้ไขแบบเต็มก่อนที่จะโพสต์) ฉันค่อนข้างแน่ใจว่ามันใช้เวลานานเกินกว่าที่ฉันจะคิดได้เมื่อเริ่มต้น แต่ถ้าฉันคิดว่าจะต้องใช้เวลานานเท่าใด :)
Eliah Kagan

6
เขียนมากขึ้นฉันต้องการหนังสือคำตอบของคุณ
Grijesh Chauhan

TL; DR แต่ทำให้ฉันหัวเราะอยู่ดี!
Fabby

ทุกอย่างตั้งแต่ชื่อถึงคำอธิบายเป็นสิ่งที่ดี ฉันเข้าใจในครั้งแรก แต่ฉันอ่านอีกครั้งเพราะฉันต้องการ
Sumeet Deshmukh

23

คุณมีแนวคิดพื้นฐานอยู่แล้ว หากคุณต้องการโค้ดนี้ในbash(ซึ่งเป็นตัวเลือกที่สมเหตุสมผลเนื่องจากเป็นเชลล์เริ่มต้นบน Ubuntu และ Linuxes อื่น ๆ ส่วนใหญ่) คุณไม่สามารถใช้งานได้caseเนื่องจากไม่เข้าใจช่วง แต่คุณสามารถใช้if/ else:

#!/usr/bin/env bash

read -p "Please enter your choice: " response

## If the response given did not consist entirely of digits
if [[ ! $response =~ ^[0-9]*$ ]]
then
    ## If it was Quit or quit, exit
    [[ $response =~ [Qq]uit ]] && exit
    ## If it wasn't quit or Quit but wasn't a number either,
    ## print an error message and quit.
    echo "Please enter a number between 0 and 100 or \"quit\" to exit" && exit
fi
## Process the other choices
if [ $response -le 59 ]
then
    echo "F"
elif [ $response -le 69 ]
then
    echo "D"
elif  [ $response -le 79 ]
then
    echo "C"
elif  [ $response -le 89 ]
then
    echo "B"
elif [ $response -le 100 ]
then
    echo "A"
elif [ $response -gt 100 ]
then
    echo "Please enter a number between 0 and 100"
     exit
fi

4
พวงของผู้ทดสอบสามารถกำจัดสันนิษฐานว่าเนื่องจากคุณกำลังใช้-ge elifและไม่มีความรัก(( $response < X ))ใช่มั้ย
muru

2
@muru จริงขอบคุณ ฉันติดอยู่ในช่วงตัวเลข แต่ก็ไม่มีเหตุผล สำหรับ(( $response < X ))แน่นอน แต่ฉันพบว่าชัดเจนและ OP นั้นเห็นได้ชัดว่าใหม่ในการทุบตีสคริปต์
terdon

12
#!/bin/bash

while true
do
  read -p "Please enter your choice: " choice

  case "$choice"
   in
      [0-9]|[1-5][0-9])
          echo "F"
          ;;
      6[0-9])
          echo "D"
          ;;
      7[0-9])
          echo "C"
          ;;
      8[0-9])
          echo "B"
          ;;
      9[0-9]|100)
          echo "A"
          ;;
      [Qq])
          exit 0
          ;;
      *) echo "Only numbers between 0..100, q for quit"
          ;;
  esac
done

และรุ่นที่กะทัดรัดยิ่งขึ้น (ขอบคุณ@EliahKagan ):

#!/usr/bin/env bash

while read -erp 'Enter numeric grade (q to quit): '; do
    case $REPLY in
        [0-9]|[1-5][0-9])   echo F ;;
        6[0-9])             echo D ;;
        7[0-9])             echo C ;;
        8[0-9])             echo B ;;
        9[0-9]|100)         echo A ;;

        [Qq])               exit ;;
        *)                  echo 'Only numbers between 0..100, q for quit' ;;
    esac
done

1
นั่นเป็นช่วงของตัวละครใช่ไหม ie [0-59]หมายถึงอักขระใด ๆ ตั้งแต่ 0,1,2,3,4,5 หรือ 9 เป็นต้นไป ฉันไม่เห็นวิธีการที่สามารถทำงานกับค่าตัวเลข
ขับขี่เหล็ก

3
คุณไม่จำเป็นต้องเป็น FGITW ตลอดเวลา ใช้เวลาเขียนคำตอบที่ดี ดูว่า terdon หรือ Eliah Kagan ทำงานอย่างไร
muru

@AB ฉันสังเกตเห็นว่าวิธีนี้สั้นลงส่วนใหญ่เกิดจากการเปลี่ยนแปลงทางโวหารในขณะที่ยังคงสามารถอ่านได้ ความกะทัดรัดเป็นสิ่งที่สำคัญที่สุดในการพิจารณาดังนั้นฉันไม่คิดว่าคุณควรเปลี่ยนสิ่งที่คุณมีในวิธีนี้ แต่เนื่องจากรูปแบบกะทัดรัดนั้นไม่ได้ใช้พื้นที่มากนักคุณอาจลองแสดงมันด้วยเช่นกันในกรณีที่ผู้อ่านบางคนต้องการสคริปต์ที่สั้นกว่าซึ่งทำงานในลักษณะเดียวกัน (หากคุณต้องการโปรดใช้เวอร์ชันย่อของฉันหรือรูปแบบใด ๆ ก็ได้)
Eliah Kagan

9

การติดตั้ง Ubuntu ทั้งหมดมี Python ดังนั้นนี่คือสคริปต์ python หนึ่งซับ หากคุณต้องการที่จะอยู่ในทุบตีฉันยังเขียนเทียบเท่าเป็นสคริปต์เปลือก

print (chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1]))))

หากต้องการเรียกใช้ให้บันทึกลงในไฟล์ (เช่นgrade.py) จากนั้นเรียกใช้ในเทอร์มินัลด้วยวิธีนี้:

python grade.py

นี่คือสิ่งที่คุณจะเห็น:

Enter the number: 65
E

มันทำงานอย่างไร

  1. รับข้อมูล - 65 .
  2. เพิ่ม 0 ถึงจุดเริ่มต้น - 065 .
  3. ลบถ่านตัวสุดท้าย - 06 .
  4. 75 ลบจำนวนนั้น - 70-
  5. แปลงเป็นตัวอักษร (A คือ 65, B คือ 66) - E-
  6. พิมพ์ออกมา - E.

สรรพนามของฉันคือเขา / เขา


ฉันชอบความคิดของคุณ +1
AB

ไม่ได้ใช้input()ก็จะเรียกeval()ใช้raw_input()instead..also การจัดลำดับของคุณไม่ถูกต้องตามที่90+จะพิมพ์เกรดB..use chr(74 - max(4, num))....
heemayl

วิธีการแก้ปัญหา well..your เป็นสิ่งที่ดีและจะทำงานใน python2 also..just เปลี่ยนแปลงinput()ไปraw_input()สำหรับ python2..thats มัน ..
heemayl

print chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1])))
heemayl

จากนั้นคุณต้องเปลี่ยนรหัสเดิมของคุณเช่นกัน .. รหัสที่คุณแก้ไขซึ่งขณะนี้มันผิดปกติ แต่python3ไม่ได้raw_input().. ฉันแนะนำraw_input()ให้คุณเริ่มต้นตามที่คุณบอกให้รันโดยใช้python2..
heemayl

6

ต่อไปนี้เป็นวิธีแก้ปัญหาทุบตีแบบกึ่งอิสระของฉันซึ่งเติมอาเรย์ด้วย 101 รายการจากนั้นตรวจสอบอินพุตของผู้ใช้ แม้สำหรับการใช้งานจริงที่สมเหตุสมผล - หากคุณต้องการประสิทธิภาพที่ยอดเยี่ยมคุณจะไม่ใช้ bash และการมอบหมายร้อย (หรือมากกว่านั้น) ก็ยังคงรวดเร็ว แต่มันจะหยุดมีเหตุผลหากขยายไปสู่ช่วงที่ใหญ่กว่า (เช่นล้าน)

#!/usr/bin/env bash
p(){ for i in `seq $2 $3`; do g[$i]=$1; done; }
p A 90 100; p B 80 89; p C 70 79; p D 60 69; p F 0 59
while read -r n && [[ ! $n =~ ^[qQ] ]]; do echo ${g[$n]}; done

ข้อดี:

  • มันไม่ได้ลึกลับจริงๆ แม้ว่ามันจะยาวกว่าโซลูชันที่สั้นที่สุด แต่ก็ไม่ค่อยเป็นเอกสารด้วยตนเองเหมือนโซลูชันที่ยาวกว่า ... มันสมเหตุสมผลในการทำเอกสารด้วยตนเองขณะที่ยังคงเด็ดบนด้านข้างเล็ก ๆ
  • มันช่วยให้การปรับเปลี่ยนง่ายในการเปลี่ยนช่วงเกรดหรือเพิ่ม / ลบเกรด
  • จะดำเนินการในวงและลาออกในq, quitหรืออะไรที่เริ่มต้นด้วยq/Q /
  • ระบุเกรดที่สูงขึ้นก่อนเพื่อช่วยให้คุณคิดในเชิงบวก :)
  • อืมมันทำงานได้ดีแม้หลังจากที่คุณมองแล้วก็มีคุณสมบัติที่สำคัญ คุณสามารถใช้สิ่งนี้ได้จริง!

ข้อเสีย:

  • มันให้ค่า F แก่คุณเมื่อคุณใส่ค่าที่ไม่ใช่ตัวเลข ... แต่นั่นก็ไม่ได้แย่มากจริง ๆ ใช่ไหม? หากคุณให้หมายเลขที่ไม่ใช่จำนวนที่ต้องการจำนวนบางทีคุณสมควรได้รับ F!
  • คลุมเครือใส่ฐานแปดอาจจะถือว่าเป็นฐานแปด (ตั้งแต่gเป็นอาร์เรย์จัดทำดัชนีหนึ่งมิติ ) อย่างที่บอกไปแล้วว่า "มันไม่ใช่บั๊กมันเป็นคุณสมบัติ!" อาจจะ
  • อินพุตที่อยู่นอกช่วงหรือไม่ตัวเลขทำให้เกิดบรรทัดว่างที่จะพิมพ์ แม้ว่าจะไม่มีอะไรผิดปกติกับมัน: มันบอกคุณว่าเกรดตัวอักษรที่สอดคล้องกับการป้อนข้อมูลของคุณและสำหรับการป้อนข้อมูลที่ผิดไม่มีหนึ่ง
  • ใส่จำนวนลบและมัน ... ดีเรียกว่าไข่อีสเตอร์ไข่อีสเตอร์
  • ยังคงยาวกว่าโซลูชันไพ ธ อนของทิมอย่างมาก ใช่ฉันไม่สามารถหมุนที่ดูเหมือนจะเป็นข้อได้เปรียบ

ค่อนข้างเย็นใช่มั้ย (ดีฉันคิดอย่างนั้น)

มันทำงานอย่างไร

  1. pฟังก์ชั่นพี opulates อาร์เรย์จัดทำดัชนีตัวเลขgของกรัม rades ที่ดัชนีตั้งแต่อาร์กิวเมนต์แรกในการที่สองด้วย (ตัวอักษร) ค่าที่กำหนดในการโต้เถียงที่สามของ
  2. p ถูกเรียกสำหรับตัวอักษรแต่ละเกรดเพื่อกำหนดช่วงตัวเลข
  3. อ่านอินพุตของผู้ใช้ตราบเท่าที่มีอยู่และไม่ได้ขึ้นต้นด้วยq(หรือQ) ตรวจสอบgอาร์เรย์ที่เกรดตัวอักษรสอดคล้องกับหมายเลขที่ป้อนและพิมพ์ตัวอักษรนั้น

เงื่อนไขนี้เป็นอย่างไร [[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
Helio

6

หลังจากทำใน Python 2ฉันตัดสินใจที่จะทำมันด้วยการทุบตี

#! /bin/bash

read -p "Enter the number: " i
i=0$i
x=$((10#${i::-1}))
printf "\x$(printf %x $((11-($x>5?$x:5)+64)))\n"

เพื่อให้ทำงานได้บันทึกไว้ในแฟ้ม (เช่น grade.sh) ทำให้มันปฏิบัติการด้วยและเรียกใช้แล้วด้วยchmod +x grade.sh./grade.sh

นี่คือสิ่งที่คุณจะเห็น:

Enter the number: 65
E

มันทำงานอย่างไร

  1. รับข้อมูล -65 .
  2. เพิ่ม 0 ถึงจุดเริ่มต้น - 065(และ10#คงไว้ที่ฐาน 10)
  3. ลบอักขระตัวสุดท้าย -06 .
  4. 75 ลบตัวเลขที่ 70-
  5. แปลงเป็นตัวอักษร (A คือ 65, B คือ 66) E-
  6. พิมพ์ออกมา - E.

สรรพนามของฉันคือเขา / เขา


ฉลาดมากทำดี
คอส

@ kos ขอบคุณ :) ฉันสงสัยว่ามันจะใช้ได้กับ OP เพราะช่วงของเขาอาจจะไม่ใช่สิ่งที่เขาโพสต์ ฉันคาดหวังสิ่งเหล่านั้นเพื่อความเรียบง่าย
ทิม

5

และนี่คือรุ่น awk ของฉัน:

awk '{
  if($_ <= 100 && $_ >= 0) {
      sub(/^([0-9]|[1-5][0-9])$/, "F", $_);
      sub(/^(6[0-9])$/, "D", $_);
      sub(/^(7[0-9])$/, "C", $_);
      sub(/^(8[0-9])$/, "B", $_);
      sub(/^(9[0-9]|100)$/, "A", $_);
      print
    }
    else {
      print "Only numbers between 0..100"
    }
}' -

หรือเป็นหนึ่งซับ:

awk '{if($_ <= 100 && $_ >= 0) { sub(/^([0-9]|[1-5][0-9])$/, "F", $_); sub(/^(6[0-9])$/, "D", $_); sub(/^(7[0-9])$/, "C", $_); sub(/^(8[0-9])$/, "B", $_);sub(/^(9[0-9]|100)$/, "A", $_);   print} else { print "Only numbers between 0..100"}}' -

4

นี่คือคำตอบ "ลึกลับ" อีกข้อ

perl -E '
    print "number: "; 
    $n = <>; 
    say qw/A A B C D E F F F F F/[11-($n+1)/10]
       if $n=~/^\s*\d/ and 0<=$n and $n<=100
'

คำอธิบาย

  • perl -E: -Eเช่น-eอนุญาตให้ส่งสคริปต์เป็นอาร์กิวเมนต์บรรทัดคำสั่ง นี่เป็นวิธีเรียกใช้ perl one-liners ซึ่งแตกต่างจาก-e, -Eยังช่วยให้คุณสมบัติเสริมทั้งหมด (เช่นsayซึ่งเป็นพื้นprintด้วยการขึ้นบรรทัดใหม่ต่อท้าย.)
  • print "number: "; : แจ้งให้ผู้ใช้ป้อนหมายเลข
  • $n = <>;: $nบันทึกตัวเลขที่เป็น

บิตต่อไปจะต้องถูกทำลายลงเล็กน้อย qw/string/ประเมินรายการที่ทำโดยทำลายstringที่ช่องว่าง ดังนั้นqw/A A B C D E F F F F F/นี่คือรายการจริง:

0 : A
1 : A
2 : B
3 : C
4 : D
5 : E
6 : F
7 : F
8 : F
9 : F
10 : F

ดังนั้นจึงsay qw/A A B C D E F F F F F/[11-($n+1)/10]เท่ากับ

my @F=("A","A","B","C","D","E","F","F","F","F","F");
print "$F[11-($n+1)/10]\n"

ตอนนี้ Perl อนุญาตให้ใช้ดัชนีเชิงลบเพื่อดึงองค์ประกอบนับจากจุดสิ้นสุดของอาร์เรย์ ตัวอย่างเช่น,$arrray[-1]จะพิมพ์องค์ประกอบสุดท้ายของอาร์เรย์ นอกจากนี้ดัชนีอาเรย์จุดลอยตัว (เช่น 10.7) จะถูกตัดให้เป็นจำนวนเต็มถัดไปโดยอัตโนมัติ (10.7 หรือ 10.3 หรืออะไรก็ตามที่กลายเป็น 10)

ผลลัพธ์ของสิ่งนี้คือดัชนี11-($n+1)/10จะประเมินองค์ประกอบที่เหมาะสม (เกรด) ของอาร์เรย์เสมอ


4
คำตอบที่ลึกลับล้วนเป็นสิ่งที่ดี แต่โปรดรวมคำอธิบายไว้ด้วย
muru

1

แม้ว่าคุณจะขอวิธีการแก้ปัญหา bash แต่ฉันคิดว่าใน python สิ่งนี้สามารถทำได้ในระยะเวลาอันสั้น ครอบคลุมทั้งข้อผิดพลาดในการจัดการในกรณีที่อินพุตไม่ถูกต้องและ "การแปลง" ของตัวเลขระหว่าง 0 ถึง 100 เป็นตัวอักษรจาก A ถึง F (หรืออื่น ๆ ):

#!/usr/bin/env python3
try:
    n = int(input("number: ")); n = n if n>0 else ""
    print("FEDCBA"[[n>=f for f in [50,60,70,80,90,101]].count(True)])
except:
    print("invalid input")

คำอธิบาย

  1. ก่อนอื่นเราต้องได้รับหมายเลขจากผู้ใช้:

    n = int(input("number: "))
  2. เราทดสอบหมายเลขนี้ให้ถูกต้องตามเงื่อนไขจำนวนหนึ่ง:

    n>=50, n>=60, n>=70, n>=80, n>=90

    สำหรับแต่ละการทดสอบเหล่านี้ผลจะเป็นอย่างใดอย่างหนึ่งหรือFalse Trueดังนั้น (บีบอัดรหัสเล็กน้อย):

    [n>=f for f in [50,60,70,80,90]].count(True)]

    จะผลิตรูปจาก0ถึง5

  3. ต่อจากนั้นเราสามารถใช้รูปนี้เป็นดัชนีสำหรับสตริงเพื่อสร้างตัวละครเป็นเอาท์พุทเช่น

    "ABCDEF"[3] 

    จะส่งออก "D" (ตั้งแต่ตัวอักษรตัวแรก = "A")

  4. เพิ่มเติม101ในรายการคือการสร้างข้อผิดพลาด (ดัชนี -) ในกรณีที่จำนวนเกินกว่า100เนื่องจาก"ABCDEF"[6]ไม่มีอยู่ เดียวกันจะไปสำหรับn = n if n>=0 else ""ซึ่งจะสร้างข้อผิดพลาด (มูลค่า) ถ้าจำนวนต่ำกว่า 0 ถูกป้อน
    ในกรณีดังกล่าวเช่นเดียวกับถ้าใส่ไม่ได้เป็นรูปผลที่จะได้รับ:

    invalid input

การทดสอบ:

number: 10
F

number: 50
E

number: 60
D

number: 70
C

number: 80
B

number: 90
A

number: 110
invalid input

number: -10
invalid input

number: Monkey
invalid input
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.