การตรวจสอบพารามิเตอร์กับสคริปต์ Bash


97

ฉันคิดขั้นพื้นฐานเพื่อช่วยให้กระบวนการลบโฟลเดอร์จำนวนหนึ่งเป็นไปโดยอัตโนมัติเมื่อไม่จำเป็น

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

สิ่งนี้เกิดขึ้นเช่นนี้:

./myscript.sh <{id-number}>

ปัญหาคือถ้าคุณลืมพิมพ์id-number (อย่างที่เคยทำไปแล้ว)มันอาจจะลบหลาย ๆ อย่างที่คุณไม่ต้องการให้ลบออกไป

มีวิธีเพิ่มการตรวจสอบความถูกต้องให้กับพารามิเตอร์บรรทัดคำสั่งหรือไม่? ในกรณีของฉันควรตรวจสอบว่า a) มีพารามิเตอร์หนึ่งตัว b) เป็นตัวเลขและ c) มีโฟลเดอร์นั้นอยู่ ก่อนดำเนินการต่อด้วยสคริปต์

คำตอบ:


163
#!/bin/sh
die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 1 ] || die "1 argument required, $# provided"
echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"

while read dir 
do
    [ -d "$dir" ] || die "Directory $dir does not exist"
    rm -rf "$dir"
done <<EOF
~/myfolder1/$1/anotherfolder 
~/myfolder2/$1/yetanotherfolder 
~/myfolder3/$1/thisisafolder
EOF

แก้ไข : ฉันพลาดส่วนที่เกี่ยวกับการตรวจสอบว่ามีไดเร็กทอรีอยู่หรือไม่ในตอนแรกดังนั้นฉันจึงเพิ่มสิ่งนั้นเข้าไปในการกรอกสคริปต์ นอกจากนี้ยังได้แก้ไขปัญหาที่เกิดขึ้นในความคิดเห็น คงที่แสดงออกปกติเปลี่ยนจากการ==eq

นี่ควรเป็นสคริปต์แบบพกพาที่รองรับ POSIX เท่าที่ฉันสามารถบอกได้ มันไม่ได้ใช้ bashisms ใด ๆ ซึ่งเป็นสิ่งสำคัญเพราะจริง/bin/shบน Ubuntu เป็นจริงวันนี้ไม่ได้dashbash


อย่าลืมตั้งค่า + e และใช้ '-eq' แทน '==' สำหรับการเปรียบเทียบจำนวนเต็ม
ปืน

เปลี่ยนเป็น -eq; set + e ซื้ออะไรให้คุณที่นี่?
Brian Campbell

ฉันพบสองสิ่งในคำตอบของฉันที่คุณอาจต้องการแก้ไขในของคุณ: อันดับแรก SO hilighter จะบ้าไปแล้วในราคา $ # (ถือว่าเป็นคำอธิบาย) ฉันทำ "$ #" เพื่อแก้ไข ประการที่สอง regex ยังตรงกับ "foo123bar" ฉันแก้ไขโดยทำ ^ [0-9] + $ คุณสามารถแก้ไขได้โดยใช้ตัวเลือก -x ของ grep
Johannes Schaub - litb

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

1
ตามคำตอบของ @Morten Nielsen ด้านล่าง grep '$ [0-9] + ^' ดูแปลกจริงๆ มันควรจะเป็น '^ [0-9] + $' ไม่ใช่หรือ
martin jakubik

21

shวิธีการแก้ปัญหาโดยBrian Campbellในขณะที่มีเกียรติและดำเนินการอย่างดีมีปัญหาเล็กน้อยดังนั้นฉันคิดว่าฉันต้องการให้ของตัวเองbashวิธีการแก้ปัญหา

ปัญหาเกี่ยวกับปัญหาsh:

  • เครื่องหมายทิลเดอร์อิน~/fooจะไม่ขยายไปยังโฮมไดเรคทอรีของคุณภายในเฮริเอกสาร และไม่เมื่ออ่านโดยreadคำสั่งหรืออ้างในrmคำสั่ง ซึ่งหมายความว่าคุณจะได้รับNo such file or directoryข้อผิดพลาด
  • การฟอร์กออกgrepและสำหรับการใช้งานพื้นฐานนั้นเป็นเรื่องบ้าคลั่ง โดยเฉพาะอย่างยิ่งเมื่อคุณใช้เปลือกหอยเส็งเคร็งเพื่อหลีกเลี่ยงการทุบตีที่ "หนัก"
  • ฉันยังสังเกตเห็นปัญหาการอ้างสิทธิ์บางอย่างเช่นเกี่ยวกับการขยายพารามิเตอร์ในechoไฟล์.
  • แม้ว่าจะหายาก แต่โซลูชันไม่สามารถรับมือกับชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ได้ (แทบจะไม่มีวิธีแก้ปัญหาใดที่shสามารถรับมือกับพวกเขาได้ - นั่นคือเหตุผลที่ฉันมักชอบbashมันกันกระสุนมากกว่าและใช้ประโยชน์ได้ยากกว่าเมื่อใช้งานได้ดี)

ในขณะที่ใช่การใช้/bin/shhashbang ของคุณหมายความว่าคุณต้องหลีกเลี่ยงbashisms โดยเสียค่าใช้จ่ายทั้งหมดคุณสามารถใช้bashisms ทั้งหมดที่คุณชอบได้แม้ใน Ubuntu หรืออะไรก็ตามเมื่อคุณซื่อสัตย์และวางไว้#!/bin/bashที่ด้านบน

ดังนั้นนี่คือbashวิธีแก้ปัญหาที่เล็กกว่าสะอาดกว่าโปร่งใสกว่าอาจ "เร็วกว่า" และกันกระสุนได้มากกว่า

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. สังเกตคำพูดรอบ ๆ$1ในrmแถลงการณ์!
  2. การ-dตรวจสอบจะล้มเหลวหาก$1ว่างเปล่านั่นคือการตรวจสอบสองครั้งในหนึ่งเดียว
  3. ฉันหลีกเลี่ยงการแสดงออกปกติด้วยเหตุผล หากคุณต้องใช้=~ใน bash คุณควรใส่นิพจน์ทั่วไปในตัวแปร ไม่ว่าในกรณีใด globs เช่นของฉันมักจะเป็นที่ต้องการและรองรับในเวอร์ชัน bash มากกว่า

1
ดังนั้นการ$1 != *[^0-9]*ทุบตีชิ้นส่วน globbing นั้นเฉพาะหรือไม่?
grinch


14

ไม่สามารถกันกระสุนได้เหมือนคำตอบข้างต้น แต่ก็ยังมีประสิทธิภาพ:

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here

11

ใช้set -uซึ่งจะทำให้การอ้างอิงอาร์กิวเมนต์ที่ไม่ได้ตั้งค่าใด ๆ ทำให้สคริปต์ล้มเหลวทันที

โปรดดูบทความ: การเขียนสคริปที่แข็งแกร่งทุบตีเชลล์ - เดวิด Pashley.com


นี่เป็นการแก้ไขที่ง่ายมากในหลาย ๆ กรณีและใช้ได้กับฟังก์ชั่นเช่นกัน ขอบคุณ!
Manfred Moser

9

man page สำหรับ test ( man test) มีตัวดำเนินการทั้งหมดที่คุณสามารถใช้เป็นตัวดำเนินการบูลีนใน bash ใช้แฟล็กเหล่านั้นในส่วนเริ่มต้นของสคริปต์ของคุณ (หรือฟังก์ชัน) สำหรับการตรวจสอบอินพุตเหมือนกับที่คุณทำในภาษาโปรแกรมอื่น ๆ ตัวอย่างเช่น:

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi

8

ใช้ '-z' เพื่อทดสอบสตริงว่างและ '-d เพื่อตรวจสอบไดเร็กทอรี

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi

2
ทำไมคุณถึงต้องการ [[]] สองเท่า?
vehomzzz

5

คุณสามารถตรวจสอบจุด a และ b อย่างกะทัดรัดได้โดยทำสิ่งต่อไปนี้:

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

ซึ่งทำให้เรา ...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

วิธีนี้ควรใช้งานได้ดีใน sh


2

การตรวจสอบอาร์กิวเมนต์ Bash หนึ่งบรรทัดโดยมีและไม่มีการตรวจสอบความถูกต้องของไดเรกทอรี

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

ซับหนึ่งอันรวดเร็วและสกปรก

: ${1?' You forgot to supply a directory name'}

เอาท์พุท:

./my_script: line 279: 1: You forgot to supply a directory name

Fancier - จัดหาชื่อฟังก์ชันและการใช้งาน

${1? ERROR Function: ${FUNCNAME[0]}() Usage: " ${FUNCNAME[0]} directory_name"}

เอาท์พุท:

./my_script: line 288: 1:  ERROR Function: deleteFolders() Usage:  deleteFolders directory_name

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

เพิ่มบรรทัดต่อไปนี้ภายในฟังก์ชันหรือสคริปต์ที่รับอาร์กิวเมนต์

: ${1?'forgot to supply a directory name'} && validate $1 || die 'Please supply a valid directory'

จากนั้นคุณสามารถสร้างฟังก์ชันการตรวจสอบความถูกต้องที่ทำสิ่งต่างๆเช่น

validate() {

    #validate input and  & return 1 if failed, 0 if succeed
    if [[ ! -d "$1" ]]; then
        return 1
    fi
}

และฟังก์ชันดายที่ยกเลิกสคริปต์เมื่อล้มเหลว

die() { echo "$*" 1>&2 ; exit 1; }

สำหรับอาร์กิวเมนต์เพิ่มเติมเพียงแค่เพิ่มบรรทัดเพิ่มเติมโดยจำลองรูปแบบ

: ${1?' You forgot to supply the first argument'}
: ${2?' You forgot to supply the second argument'}

1

โพสต์เก่า แต่ฉันคิดว่าฉันสามารถมีส่วนร่วมได้

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

  1. ป่าทุกที่ที่ตรงกัน ให้ลบเหตุการณ์ย่อย "โฟลเดอร์"

    $ rm -rf ~/*/folder/*
    
  2. เชลล์ย้ำ ให้ลบโฟลเดอร์ก่อนและหลังเฉพาะด้วยบรรทัดเดียว

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  3. เชลล์วนซ้ำ + var (ทดสอบ BASH แล้ว)

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.