У цій відповіді є рішення для будь-якого типу файлів. З новими рядками чи пробілами.
Існують рішення як для недавнього, так і для давнього башмаку та навіть старих оболонок пози.
Дерево, перелічене нижче в цій відповіді [1] , використовується для тестів.
виберіть
Легко приступити select
до роботи або з масивом:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
Або з позиційними параметрами:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
Отже, єдина реальна проблема - отримати "список файлів" (правильно розмежований) всередині масиву або всередині позиційних параметрів. Продовжуйте читати.
баш
Я не бачу проблеми, про яку ви повідомляєте bash. Bash може шукати всередині заданого каталогу:
$ 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>
Або, якщо вам подобається цикл:
$ 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>
Зауважте, що вищевказаний синтаксис буде правильно працювати з будь-якою (розумною) оболонкою (не принаймні csh).
Єдине обмеження, яке має синтаксис вище, - це спуститися до інших каталогів.
Але Баш міг це зробити:
$ 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>
Щоб вибрати лише деякі файли (наприклад, ті, що закінчуються у файлі), просто замініть *:
$ 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>
міцний
Коли ви розміщуєте " безпечний для космосу " в заголовку, я припускаю, що те, що ви мали на увазі, " надійне ".
Найпростіший спосіб бути надійним щодо пробілів (або нових рядків) - відхилити обробку вводу, який містить пробіли (або нові рядки). Дуже простий спосіб зробити це в оболонці - це вихід з помилкою, якщо будь-яке ім'я файлу розширюється пробілом. Існує кілька способів зробити це, але найбільш компактний (і posix) (але обмежений одним вмістом каталогів, включаючи назви судових каталогів та уникаючи точкових файлів):
$ 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.
Якщо застосовуваний розчин є надійним у будь-якому з цих предметів, видаліть тест.
В bash, підкаталоги можуть бути протестовані одразу з ** поясненим вище.
Є кілька способів включити крапкові файли, рішення Posix таке:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
знайти
Якщо знахідку потрібно використовувати з якоїсь причини, замініть роздільник на NUL (0x00).
баш 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>
баш 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[@]}"
ПОСІБНО
Для того, щоб зробити дійсне рішення POSIX, коли знахідка не має роздільника NUL і немає -d
(ні -a
) для читання, нам потрібен цілком різний підхід.
Нам потрібно використовувати комплекс -exec
від пошуку з викликом до оболонки:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
Або, якщо потрібно - це вибір (select є частиною bash, не 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] Це дерево (\ 012 - це нові рядки):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
Можна побудувати за допомогою цих двох команд:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}