Cập nhật 2020 cho người dùng Linux:
Nếu bạn có một phiên bản up-to-date của bash (4,4-alpha hoặc tốt hơn), như bạn có thể làm gì nếu bạn đang trên Linux, sau đó bạn nên sử dụng câu trả lời Benjamin W. .
Nếu bạn đang sử dụng Mac OS, mà — lần trước tôi đã kiểm tra — vẫn sử dụng bash 3.2 hoặc đang sử dụng bash cũ hơn, thì hãy tiếp tục sang phần tiếp theo.
Câu trả lời cho bash 4.3 trở xuống
Đây là một giải pháp để nhận đầu ra của find
một bash
mảng:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Điều này khá phức tạp vì nói chung, tên tệp có thể có khoảng trắng, dòng mới và các ký tự thù địch với tập lệnh khác. Cách duy nhất để sử dụng find
và để các tên tệp được tách biệt với nhau một cách an toàn là sử dụng cách -print0
in các tên tệp được phân tách bằng ký tự rỗng. Điều này sẽ không có gì bất tiện nếu bash's readarray
/ mapfile
functions hỗ trợ các chuỗi được phân tách bằng null nhưng chúng thì không. Bash's read
làm và điều đó dẫn chúng ta đến vòng lặp ở trên.
[Câu trả lời này ban đầu được viết vào năm 2014. Nếu bạn có phiên bản bash gần đây, vui lòng xem bản cập nhật bên dưới.]
Làm thế nào nó hoạt động
Dòng đầu tiên tạo một mảng trống: array=()
Mỗi khi read
câu lệnh được thực thi, tên tệp được phân tách bằng null sẽ được đọc từ đầu vào chuẩn. Các -r
tùy chọn cho read
rời ký tự gạch chéo ngược một mình. Cho -d $'\0'
biết read
rằng đầu vào sẽ được phân tách bằng null. Kể từ khi chúng tôi bỏ qua tên để read
, vỏ đặt đầu vào cho tên mặc định: REPLY
.
Câu array+=("$REPLY")
lệnh nối tên tệp mới vào mảng array
.
Dòng cuối cùng kết hợp chuyển hướng và thay thế lệnh để cung cấp đầu ra find
cho đầu vào chuẩn của while
vòng lặp.
Tại sao sử dụng thay thế quy trình?
Nếu chúng tôi không sử dụng thay thế quy trình, vòng lặp có thể được viết là:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
Trong phần trên, đầu ra của find
được lưu trữ trong một tệp tạm thời và tệp đó được sử dụng làm đầu vào tiêu chuẩn cho vòng lặp while. Ý tưởng của việc thay thế quy trình là làm cho các tệp tạm thời như vậy không cần thiết. Vì vậy, thay vì để while
vòng lặp lấy stdin của nó tmpfile
, chúng ta có thể yêu cầu nó lấy stdin từ đó <(find . -name ${input} -print0)
.
Thay thế quy trình rất hữu ích. Ở nhiều nơi mà lệnh muốn đọc từ tệp, bạn có thể chỉ định thay thế tiến trình <(...)
, thay vì tên tệp. Có một dạng tương tự, >(...)
có thể được sử dụng thay cho tên tệp nơi lệnh muốn ghi vào tệp.
Giống như mảng, thay thế quy trình là một tính năng của bash và các trình bao nâng cao khác. Nó không phải là một phần của tiêu chuẩn POSIX.
Thay thế: lastpipe
Nếu muốn, lastpipe
có thể được sử dụng thay thế cho quá trình thay thế (đầu mũ: Caesar ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
yêu cầu bash chạy lệnh cuối cùng trong đường dẫn trong trình bao hiện tại (không phải nền). Bằng cách này, phần array
còn lại tồn tại sau khi đường ống hoàn thành. Bởi vì lastpipe
chỉ có hiệu lực nếu điều khiển công việc bị tắt, chúng tôi chạy set +m
. (Trong một tập lệnh, trái ngược với dòng lệnh, điều khiển công việc bị tắt theo mặc định.)
Ghi chú bổ sung
Lệnh sau tạo một biến shell, không phải một mảng shell:
array=`find . -name "${input}"`
Nếu bạn muốn tạo một mảng, bạn sẽ cần đặt các parens xung quanh đầu ra của tìm kiếm. Vì vậy, một cách ngây thơ, người ta có thể:
array=(`find . -name "${input}"`)
Vấn đề là trình bao thực hiện tách từ trên các kết quả find
để các phần tử của mảng không được đảm bảo là những gì bạn muốn.
Cập nhật 2019
Bắt đầu với phiên bản 4.4-alpha, bash hiện hỗ trợ một -d
tùy chọn để vòng lặp trên không còn cần thiết nữa. Thay vào đó, người ta có thể sử dụng:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Để biết thêm thông tin về vấn đề này, vui lòng xem (và upvote) câu trả lời Benjamin W. .