Xác thực các tham số cho một tập lệnh Bash


95

Tôi đã nghĩ ra một cách cơ bản để giúp tự động hóa quá trình xóa một số thư mục khi chúng trở nên không cần thiết.

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

Điều này được gợi lên như vậy:

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

Vấn đề là nếu bạn quên nhập id-number (như tôi đã làm lúc đó) , thì nó có thể xóa rất nhiều thứ mà bạn thực sự không muốn xóa.

Có cách nào bạn có thể thêm bất kỳ hình thức xác thực nào vào các tham số dòng lệnh không? Trong trường hợp của tôi, tốt hơn hết là bạn nên kiểm tra xem a) có một tham số, b) là số và c) thư mục đó tồn tại; trước khi tiếp tục với kịch bản.

Câu trả lời:


158
#!/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

chỉnh sửa : Tôi đã bỏ lỡ phần kiểm tra xem các thư mục có tồn tại lúc đầu hay không, vì vậy tôi đã thêm phần đó vào, hoàn thành tập lệnh. Ngoài ra, đã giải quyết các vấn đề được nêu trong các bình luận; đã sửa biểu thức chính quy, chuyển từ ==sang eq.

Đây phải là một tập lệnh di động, tuân thủ POSIX theo như tôi có thể nói; nó không sử dụng bất kỳ cơ sở nào, điều này thực sự quan trọng bởi vì /bin/shtrên Ubuntu thực sự là dashnhững ngày này, không bash.


nhớ để thiết lập + e và sử dụng '-eq' thay vì '==' để so sánh số nguyên
súng

Đã thay đổi nó thành -eq; cái gì set + e mua cho bạn ở đây?
Brian Campbell,

Tôi đã tìm thấy hai điều trong câu trả lời của mình mà bạn cũng có thể muốn sửa chữa trong câu trả lời của mình: đầu tiên là dấu hiệu SO phát điên vì $ # (coi nó như một bình luận). tôi đã làm "$ #" để sửa nó. thứ hai, regex cũng khớp với "foo123bar". tôi đã sửa nó bằng cách thực hiện ^ [0-9] + $. bạn cũng có thể sửa chữa nó bằng cách sử dụng tùy chọn -x grep của
Johannes Schaub - litb

1
@ojblass Tôi đã thiếu một trong những bài kiểm tra mà anh ấy đang hỏi. Thêm điều đó vào cũng có nghĩa là thêm vào các thư mục của anh ấy để kiểm tra, điều này đã mở rộng đáng kể kích thước của câu trả lời vì chúng không thể nằm gọn trên một dòng. Bạn có thể đề xuất một cách nhỏ gọn hơn để kiểm tra sự tồn tại của từng thư mục không?
Brian Campbell,

1
theo câu trả lời-bình luận của @Morten Nielsen bên dưới, grep '$ [0-9] + ^' thực sự trông rất lạ. Có phải là '^ [0-9] + $' không?
martin jakubik

21

Các shgiải pháp bằng cách Brian Campbell, trong khi cao quý và cũng thực hiện, có một vài vấn đề, vì vậy tôi nghĩ rằng tôi muốn cung cấp riêng tôi bashgiải pháp.

Các vấn đề với shmột trong những:

  • Dấu ngã trong ~/fookhông mở rộng đến thư mục trung gian của bạn bên trong heredocs. Và cả khi nó được đọc bởi readtuyên bố hoặc được trích dẫn trong rmtuyên bố. Có nghĩa là bạn sẽ gặp No such file or directorylỗi.
  • Forking off grepvà như vậy đối với các hoạt động cơ bản là quá khó. Đặc biệt là khi bạn đang sử dụng một vỏ crappy để tránh bị nặng "nặng".
  • Tôi cũng nhận thấy một số vấn đề trích dẫn, ví dụ xung quanh việc mở rộng tham số trong của anh ấy echo.
  • Mặc dù hiếm, nhưng giải pháp không thể đối phó với tên tệp có chứa dòng mới. (Hầu như không có giải pháp nào shcó thể đối phó với chúng - đó là lý do tại sao tôi hầu như luôn thích bash, nó chống đạn hơn nhiều và khó khai thác hơn khi được sử dụng tốt).

Mặc dù, có, việc sử dụng /bin/shhashbang của bạn có nghĩa là bạn phải tránh các bashisms bằng mọi giá, bạn có thể sử dụng tất cả các bashisms mà bạn thích, ngay cả trên Ubuntu hay không khi bạn thành thật và đặt #!/bin/bashở đầu.

Vì vậy, đây là một bashgiải pháp nhỏ hơn, sạch hơn, trong suốt hơn, có thể "nhanh hơn" và chống đạn tốt hơn.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. Chú ý đến các trích dẫn xung quanh $1trong rmtuyên bố!
  2. Các -dkiểm tra cũng sẽ thất bại nếu $1là trống rỗng, vì vậy đó là hai kiểm tra trong một.
  3. Tôi đã tránh các cụm từ thông dụng là có lý do. Nếu bạn phải sử dụng =~trong bash, bạn nên đặt biểu thức chính quy vào một biến. Trong mọi trường hợp, các quả cầu như của tôi luôn được ưu tiên và hỗ trợ trong các phiên bản bash hơn nhiều.

1
Vậy thì miếng đánh bóng đêm có $1 != *[^0-9]*cụ thể không?
grinch

15

Tôi sẽ sử dụng bash's [[:

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

Tôi thấy câu hỏi thường gặp này là một nguồn thông tin tốt.


13

Không chống đạn như câu trả lời trên, tuy nhiên vẫn hiệu quả:

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

# rm commands go here


9

Trang man cho test ( man test) cung cấp tất cả các toán tử có sẵn mà bạn có thể sử dụng làm toán tử boolean trong bash. Sử dụng các cờ đó ở đầu tập lệnh (hoặc các hàm) của bạn để xác thực đầu vào giống như bạn làm trong bất kỳ ngôn ngữ lập trình nào khác. Ví dụ:

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

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

8

Sử dụng '-z' để kiểm tra các chuỗi trống và '-d để kiểm tra các thư mục.

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
tại sao bạn cần gấp đôi [[]]?
vehomzzz

5

Bạn có thể xác thực điểm a và b một cách gọn gàng bằng cách thực hiện một số việc như sau:

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

Điều này cho chúng ta ...

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

$ ./testparams.sh 1234
1234

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

Phương pháp này sẽ hoạt động tốt trong sh.


2

một xác thực đối số Bash lót, có và không xác thực thư mục

Đây là một số phương pháp đã làm việc cho tôi. Bạn có thể sử dụng chúng trong không gian tên tập lệnh toàn cục (nếu trong không gian tên chung, bạn không thể tham chiếu đến các biến nội trang của hàm)

nhanh chóng và bẩn một lớp lót

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

đầu ra:

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

Fancier - tên chức năng cung cấp và cách sử dụng

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

đầu ra:

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

Thêm logic xác thực phức tạp mà không làm lộn xộn chức năng hiện tại của bạn

Thêm dòng sau trong hàm hoặc tập lệnh nhận đối số.

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

Sau đó, bạn có thể tạo một hàm xác thực thực hiện điều gì đó như

validate() {

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

và một hàm chết hủy bỏ tập lệnh khi bị lỗi

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

Đối với các đối số bổ sung, chỉ cần thêm một dòng bổ sung, sao chép định dạng.

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

1

Bài cũ nhưng tôi nghĩ tôi vẫn có thể đóng góp.

Một tập lệnh được cho là không cần thiết và với một số dung sai đối với các thẻ đại diện có thể được thực hiện từ dòng lệnh.

  1. hoang dã bất cứ nơi nào phù hợp. Cho phép loại bỏ bất kỳ sự xuất hiện nào của "thư mục" phụ

    $ rm -rf ~/*/folder/*
  2. Shell lặp lại. Cho phép xóa các thư mục trước và sau cụ thể bằng một dòng

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
  3. Shell đã lặp lại + var (đã thử nghiệm BASH).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.