Diese Antwort bietet Lösungen für alle Arten von Dateien. Mit Zeilenumbrüchen oder Leerzeichen.
Es gibt Lösungen für aktuelle Bash sowie alte Bash- und sogar alte Posix-Shells.
Der in dieser Antwort unten aufgeführte Baum [1] wird für die Tests verwendet.
wählen
Es ist einfach, select
mit einem Array zu arbeiten:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
Oder mit den Positionsparametern:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
Das einzige wirkliche Problem besteht also darin, die "Liste der Dateien" (korrekt abgegrenzt) in einem Array oder in den Positionsparametern abzurufen. Weiter lesen.
Bash
Ich sehe das Problem, das Sie mit Bash melden, nicht. Bash kann in einem bestimmten Verzeichnis suchen:
$ 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>
Oder, wenn Sie eine Schleife mögen:
$ 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>
Beachten Sie, dass die obige Syntax mit jeder (vernünftigen) Shell korrekt funktioniert (zumindest nicht mit csh).
Die obige Syntax beschränkt sich nur darauf, in andere Verzeichnisse zu gelangen.
Aber bash könnte das tun:
$ 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>
Um nur einige Dateien auszuwählen (wie die, die auf file enden ), ersetzen Sie einfach das *:
$ 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>
robust
Wenn Sie einen „raum- platzieren sicher “ in den Titel, ich gehe davon aus, dass , was Sie meinten „war robust “.
Die einfachste Möglichkeit, Leerzeichen (oder Zeilenumbrüche) zu vermeiden, besteht darin, die Verarbeitung von Eingaben mit Leerzeichen (oder Zeilenumbrüchen) abzulehnen. Eine sehr einfache Möglichkeit, dies in der Shell zu tun, besteht darin, mit einem Fehler zu beenden, wenn ein Dateiname mit einem Leerzeichen erweitert wird. Es gibt mehrere Möglichkeiten, dies zu tun, aber die kompakteste (und posix) (jedoch auf einen Verzeichnisinhalt beschränkt, einschließlich suddirectories-Namen und Vermeiden von Punktdateien) ist:
$ 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.
Wenn die verwendete Lösung in einem dieser Punkte robust ist, entfernen Sie den Test.
In bash konnten Unterverzeichnisse sofort mit dem oben erläuterten ** getestet werden.
Es gibt verschiedene Möglichkeiten, Punktdateien einzuschließen. Die Posix-Lösung lautet:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
finden
Wenn find aus irgendeinem Grund verwendet werden muss, ersetzen Sie das Trennzeichen durch eine 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[@]}"
POSIXLY
Um eine gültige POSIX-Lösung zu erstellen, bei der find kein NUL-Trennzeichen hat und es kein -d
(noch -a
) lesbares gibt, benötigen wir einen völlig anderen Ansatz.
Wir müssen einen Komplex -exec
aus find mit einem Aufruf an eine Shell verwenden:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
Oder wenn ein select benötigt wird (select ist Teil von bash, nicht 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] Dieser Baum (die \ 012 sind Zeilenumbrüche):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
Könnte mit diesen beiden Befehlen erstellt werden:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}