Bash Globing và tranh luận đi qua


8

Tôi có tập lệnh bash đơn giản hóa sau đây

#!/bin/bash

files=("$@")

if [ "X$files" = "X" ]; then
  files=$HOME/print/*.pdf;
fi

for file in "${files[@]}"; do
  ls "$file";
done

Nếu tôi truyền đối số (tên tệp) làm tham số thì tập lệnh này sẽ in tên tệp thích hợp. Mặt khác, nếu tôi không truyền đối số, nó sẽ in

/home/user/print/*.pdf: No such file or directory

Tại sao tên tệp không được mở rộng trong trường hợp này và làm cách nào để sửa nó? Lưu ý rằng tôi sử dụng các cấu trúc files=("$@")"${files[@]}"bởi vì tôi đọc rằng nó sẽ được ưu tiên hơn "files = $ *" thông thường.


Ở đâu files=$*bao giờ bình thường ? Điều đó hoàn toàn sai .
Stéphane Chazelas

Thông thường là tương đối, phải. Tôi có nghĩa là một phương pháp không sử dụng mảng. Bạn sẽ làm gì sau đó?
highsciguy

Câu trả lời:


10

Bạn đang gán filesnhư một biến vô hướng thay vì biến mảng .

Trong

 files=$HOME/print/*.pdf

Bạn đang gán một số chuỗi như /home/highsciguy/print/*.pdfbiến $filesvô hướng (hay còn gọi là chuỗi).

Sử dụng:

files=(~/print/*.pdf)

hoặc là

files=("$HOME"/print/*.pdf)

thay thế. Shell sẽ mở rộng mô hình toàn cầu đó thành một danh sách các đường dẫn tệp và gán từng đường dẫn đó cho các phần tử của $files mảng .

Việc mở rộng toàn cầu được thực hiện tại thời điểm chuyển nhượng.

Bạn không phải sử dụng các tính năng sh không chuẩn và bạn có thể sử dụng hệ thống của mình shthay vì bashở đây bằng cách viết nó:

#!/bin/sh -

[ "$#" -gt 0 ] || set -- ~/print/*.pdf

for file do
  ls -d -- "$file"
done

setlà để gán các "$@"mảng của các tham số vị trí.

Một cách tiếp cận khác có thể là lưu trữ mẫu hình cầu trong một biến vô hướng:

files=$HOME/print/*.pdf

Và có vỏ mở rộng toàn cầu tại thời điểm $files biến được mở rộng.

IFS= # disable word splitting
for file in $files; do ...

Ở đây, vì $fileskhông được trích dẫn (mà bạn không thường làm), việc mở rộng của nó có thể bị chia tách từ (mà chúng tôi đã vô hiệu hóa ở đây) và tạo tên tập tin / tên tập tin.

Vì vậy, *.pdf sẽ được mở rộng vào danh sách các tập tin phù hợp. Tuy nhiên, nếu $HOMEchứa các ký tự đại diện, chúng cũng có thể được mở rộng, đó là lý do tại sao vẫn nên sử dụng biến mảng.


4

Bạn có thể đã thấy những thứ như files=$*files=~/print/*.pdftrong các vỏ cũ hơn mà không có mảng, và sau đó ls $files.

Một sự thay thế biến không nằm trong dấu ngoặc kép diễn giải giá trị của biến dưới dạng một danh sách các mẫu ký tự đại diện được phân tách bằng khoảng trắng được thay thế bằng tên tệp phù hợp nếu có. Ví dụ, sau đó files=~/print/*.pdf, ls $filesmở rộng thành một cái gì đó giống như lsvới các đối số /home/highsciguy/print/bar.pdf, /home/highsciguy/print/foo.pdfv.v. Trong trường hợp files=$*, phép gán này kết hợp các đối số được truyền cho tập lệnh với khoảng trắng ở giữa và ls $filestách chúng ra.

Tất cả điều này bị phá vỡ nếu bạn có tên tệp chứa các ký tự khoảng trắng hoặc toàn cầu, đó là lý do tại sao bạn không nên làm mọi thứ theo cách này. Sử dụng mảng thay thế.

files=("$@")
if ((${#files[@]} == 0)); then
  files=("$HOME"/print/*.pdf)
fi

Lưu ý rằng

  • Tất cả các phép gán mảng yêu cầu dấu ngoặc quanh các giá trị mảng : var=(…).
  • Để kiểm tra xem một mảng có trống không, kiểm tra độ dài của nó. "$files"trống khi fileslà một mảng có phần tử của chỉ số 0 không được đặt hoặc một chuỗi trống. Cũng [ "X$foo" = "X" ]là một cách lỗi thời để kiểm tra xem $foocó trống không: tất cả các shell hiện đại thực hiện [ -n "$foo" ]chính xác. Trong bash, bạn có thể sử dụng [[ -n $foo ]].

Trong các shell không hỗ trợ mảng, trên thực tế có một mảng: các tham số vị trí cho shell hoặc hàm hiện tại. Ở đây, bạn không thực sự cần filesmảng, trên thực tế sẽ dễ sử dụng các tham số vị trí hơn.

#!/bin/sh
if [ "$#" -eq 0 ]; then
  set -- ~/print/*.pdf
fi
for file do 
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.