Một cách mạnh mẽ trong bash là mở rộng thành một mảng và chỉ xuất phần tử đầu tiên:
pattern="*.txt"
files=( $pattern )
echo "${files[0]}" # printf is safer!
(Bạn thậm chí có thể echo $files
, một chỉ mục bị thiếu được coi là [0].)
Điều này xử lý an toàn không gian / tab / dòng mới và các siêu ký tự khác khi mở rộng tên tệp. Lưu ý rằng cài đặt ngôn ngữ có hiệu lực có thể thay đổi "đầu tiên" là gì.
Bạn cũng có thể thực hiện việc này một cách tương tác với chức năng hoàn thành bash :
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]} # string to expand
if compgen -G "$cur*" > /dev/null; then
local files=( ${cur:+$cur*} ) # don't expand empty input as *
[ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
fi
}
complete -o bashdefault -F _echo echo
Điều này liên kết _echo
hàm để hoàn thành các đối số cho echo
lệnh (ghi đè hoàn thành bình thường). Một "*" bổ sung được thêm vào mã ở trên, bạn chỉ cần nhấn tab vào tên tệp một phần và hy vọng điều đúng sẽ xảy ra.
Mã này hơi phức tạp, thay vì đặt hoặc giả định nullglob
( shopt -s nullglob
) chúng tôi kiểm tra compgen -G
có thể mở rộng toàn cầu thành một số kết quả khớp, sau đó chúng tôi mở rộng an toàn thành một mảng và cuối cùng đặt TÍNH TOÁN để trích dẫn được mạnh mẽ.
Bạn có thể thực hiện một phần việc này (lập trình mở rộng toàn cầu) bằng bash compgen -G
, nhưng nó không mạnh vì nó xuất ra không được trích dẫn cho thiết bị xuất chuẩn.
Như thường lệ, việc hoàn thành khá khó khăn, điều này phá vỡ sự hoàn thành của những thứ khác, bao gồm các biến môi trường (xem _bash_def_completion()
hàm ở đây để biết chi tiết mô phỏng hành vi mặc định).
Bạn cũng có thể chỉ sử dụng compgen
bên ngoài chức năng hoàn thành:
files=( $(compgen -W "$pattern") )
Một điểm cần lưu ý là "~" không phải là một quả địa cầu, nó được xử lý bằng bash trong một giai đoạn mở rộng riêng biệt, cũng như các biến $ và các mở rộng khác. compgen -G
chỉ có tên tập tin toàn cầu, nhưng compgen -W
cung cấp cho bạn tất cả các mở rộng mặc định của bash, mặc dù có thể có quá nhiều bản mở rộng (bao gồm ``
và $()
). Không giống như -G
, -W
được trích dẫn một cách an toàn (tôi không thể giải thích sự chênh lệch). Vì mục đích của -W
nó là mở rộng mã thông báo, điều này có nghĩa là nó sẽ mở rộng "a" thành "a" ngay cả khi không có tệp nào như vậy tồn tại, vì vậy có lẽ nó không lý tưởng.
Điều này dễ hiểu hơn, nhưng có thể có tác dụng phụ không mong muốn:
_echo() {
local cur=${COMP_WORDS[COMP_CWORD]}
local files=( $(compgen -W "$cur") )
printf -v COMPREPLY %q "${files[0]}"
}
Sau đó:
touch $'curious \n filename'
echo curious*
tab
Lưu ý việc sử dụng printf %q
để trích dẫn một cách an toàn các giá trị.
Một tùy chọn cuối cùng là sử dụng đầu ra được phân tách bằng 0 với các tiện ích GNU (xem Câu hỏi thường gặp về bash ):
pattern="*.txt"
while IFS= read -r -d $'\0' filename; do
printf '%q' "$filename";
break;
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )
Tùy chọn này cung cấp cho bạn thêm một chút quyền kiểm soát đối với thứ tự sắp xếp (thứ tự khi mở rộng toàn cầu sẽ tùy thuộc vào địa phương của bạn / LC_COLLATE
và có thể hoặc không thể gấp lại trường hợp), nhưng nếu không thì là một cái búa khá lớn cho một vấn đề nhỏ như vậy ;-)