Câu trả lời này có giải pháp cho bất kỳ loại tập tin. Với dòng mới hoặc không gian.
Có những giải pháp cho bash gần đây cũng như bash cổ và thậm chí cả vỏ posix cũ.
Cây được liệt kê dưới đây trong câu trả lời này [1] được sử dụng cho các bài kiểm tra.
lựa chọn
Thật dễ dàng để select
làm việc với một mảng:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
Hoặc với các tham số vị trí:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
Vì vậy, vấn đề thực sự duy nhất là lấy "danh sách các tệp" (được phân tách chính xác) bên trong một mảng hoặc bên trong Tham số vị trí. Hãy đọc tiếp.
bash
Tôi không thấy vấn đề bạn báo cáo với bash. Bash có thể tìm kiếm trong một thư mục nhất định:
$ dir='deep/inside/a/dir'
$ printf '<%s>\n' "$dir"/*
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Hoặc, nếu bạn thích một vòng lặp:
$ set -- "$dir"/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Lưu ý rằng cú pháp ở trên sẽ hoạt động chính xác với bất kỳ shell (hợp lý) nào (không phải csh ít nhất).
Giới hạn duy nhất mà cú pháp trên có là đi xuống các thư mục khác.
Nhưng bash có thể làm điều đó:
$ shopt -s globstar
$ set -- "$dir"/**/*
$ for f; do printf '<%s>\n' "$f"; done
<deep/inside/a/dir/directory>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/zz last file>
Để chỉ chọn một số tệp (như những tệp kết thúc trong tệp), chỉ cần thay thế *:
$ set -- "$dir"/**/*file
$ printf '<%s>\n' "$@"
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/directory/zz last file>
<deep/inside/a/dir/file>
<deep/inside/a/dir/zz last file>
mạnh mẽ
Khi bạn đặt một "không gian an toàn " trong tiêu đề, tôi sẽ cho rằng những gì bạn muốn nói là " mạnh mẽ ".
Cách đơn giản nhất để mạnh mẽ về không gian (hoặc dòng mới) là từ chối xử lý đầu vào có khoảng trắng (hoặc dòng mới). Một cách rất đơn giản để thực hiện việc này trong trình bao là thoát với lỗi nếu bất kỳ tên tệp nào mở rộng với khoảng trắng. Có một số cách để làm điều này, nhưng nhỏ gọn nhất (và posix) (nhưng giới hạn trong một nội dung thư mục, bao gồm tên suddirectories và tránh các tệp dấu chấm) là:
$ set -- "$dir"/file* # read the directory
$ a="$(printf '%s' "$@" x)" # make it a long string
$ [ "$a" = "${a%% *}" ] || echo "exit on space" # if $a has an space.
$ nl='
' # define a new line in the usual posix way.
$ [ "$a" = "${a%%"$nl"*}" ] || echo "exit on newline" # if $a has a newline.
Nếu giải pháp được sử dụng là mạnh mẽ trong bất kỳ mục nào trong số đó, hãy xóa bài kiểm tra.
Trong bash, các thư mục con có thể được kiểm tra cùng một lúc với ** được giải thích ở trên.
Có một số cách để bao gồm các tệp chấm, giải pháp Posix là:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
tìm thấy
Nếu tìm thấy phải được sử dụng vì một số lý do, thay thế dấu phân cách bằng NUL (0x00).
bash 4.4+
$ readarray -t -d '' arr < <(find "$dir" -type f -name file\* -print0)
$ printf '<%s>\n' "${arr[@]}"
<deep/inside/a/dir/file name>
<deep/inside/a/dir/file with a
newline>
<deep/inside/a/dir/directory/file name>
<deep/inside/a/dir/directory/file with a
newline>
<deep/inside/a/dir/directory/file>
<deep/inside/a/dir/file>
bash 2.05+
i=1 # lets start on 1 so it works also in zsh.
while IFS='' read -d '' val; do
arr[i++]="$val";
done < <(find "$dir" -type f -name \*file -print0)
printf '<%s>\n' "${arr[@]}"
VỊ TRÍ
Để thực hiện một giải pháp POSIX hợp lệ trong đó find không có dấu phân cách NUL và không có -d
(cũng không có -a
) để đọc, chúng ta cần một aproach hoàn toàn khác.
Chúng ta cần sử dụng một phức hợp -exec
từ find với một cuộc gọi đến shell:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
Hoặc, nếu điều cần thiết là chọn (chọn là một phần của bash, không phải sh):
$ find "$dir" -type f -exec bash -c '
select f; do echo "<$f>"; break; done ' bash {} +
1) deep/inside/a/dir/file name
2) deep/inside/a/dir/zz last file
3) deep/inside/a/dir/file with a
newline
4) deep/inside/a/dir/directory/file name
5) deep/inside/a/dir/directory/zz last file
6) deep/inside/a/dir/directory/file with a
newline
7) deep/inside/a/dir/directory/file
8) deep/inside/a/dir/file
#? 3
<deep/inside/a/dir/file with a
newline>
[1] Cây này (\ 012 là dòng mới):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
Có thể được xây dựng với hai lệnh này:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}