Esta respuesta tiene soluciones para cualquier tipo de archivos. Con nuevas líneas o espacios.
Hay soluciones para bash recientes, así como bash antiguas e incluso conchas posix antiguas.
El árbol que se detalla a continuación en esta respuesta [1] se utiliza para las pruebas.
Seleccione
Es fácil ponerse select
a trabajar con una matriz:
$ dir='deep/inside/a/dir'
$ arr=( "$dir"/* )
$ select var in "${arr[@]}"; do echo "$var"; break; done
O con los parámetros posicionales:
$ set -- "$dir"/*
$ select var; do echo "$var"; break; done
Entonces, el único problema real es obtener la "lista de archivos" (delimitada correctamente) dentro de una matriz o dentro de los Parámetros Posicionales. Sigue leyendo.
golpetazo
No veo el problema que reportas con bash. Bash puede buscar dentro de un directorio dado:
$ 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>
O, si te gusta un bucle:
$ 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>
Tenga en cuenta que la sintaxis anterior funcionará correctamente con cualquier shell (razonable) (no al menos csh).
El único límite que tiene la sintaxis anterior es descender a otros directorios.
Pero bash podría hacer eso:
$ 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>
Para seleccionar solo algunos archivos (como los que terminan en archivo) simplemente reemplace el *:
$ 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>
robusto
Cuando coloque un "espacio seguro " en el título, voy a suponer que lo que quiso decir era " robusto ".
La forma más sencilla de ser robusto con respecto a los espacios (o nuevas líneas) es rechazar el procesamiento de la entrada que tiene espacios (o nuevas líneas). Una forma muy simple de hacer esto en el shell es salir con un error si algún nombre de archivo se expande con un espacio. Hay varias formas de hacer esto, pero la más compacta (y posix) (pero limitada al contenido de un directorio, incluidos los nombres de directorios y evitar los archivos de puntos) es:
$ 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.
Si la solución utilizada es robusta en alguno de esos elementos, elimine la prueba.
En bash, los subdirectorios podrían probarse de inmediato con el ** explicado anteriormente.
Hay un par de formas de incluir archivos de puntos, la solución de Posix es:
set -- "$dir"/* "$dir"/.[!.]* "$dir"/..?*
encontrar
Si debe usarse find por alguna razón, reemplace el delimitador con un 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
Para hacer una solución POSIX válida donde find no tiene un delimitador NUL y no hay -d
(ni -a
) para leer, necesitamos un enfoque completamente diferente.
Necesitamos usar un complejo -exec
de find con una llamada a un shell:
find "$dir" -type f -exec sh -c '
for f do
echo "<$f>"
done
' sh {} +
O, si lo que se necesita es un select (select es parte de bash, no 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] Este árbol (los \ 012 son líneas nuevas):
$ tree
.
└── deep
└── inside
└── a
└── dir
├── directory
│ ├── file
│ ├── file name
│ └── file with a \012newline
├── file
├── file name
├── otherfile
├── with a\012newline
└── zz last file
Podría construirse con estos dos comandos:
$ mkdir -p deep/inside/a/dir/directory/
$ touch deep/inside/a/dir/{,directory/}{file{,\ {name,with\ a$'\n'newline}},zz\ last\ file}