Dados estos nombres de archivo:
$ ls -1
file
file name
otherfile
bash
en sí mismo funciona perfectamente bien con espacios en blanco incrustados:
$ for file in *; do echo "$file"; done
file
file name
otherfile
$ select file in *; do echo "$file"; done
1) file
2) file name
3) otherfile
#?
Sin embargo, a veces es posible que no quiera trabajar con cada archivo, o incluso estrictamente $PWD
, que es donde find
entra. Lo que también maneja los espacios en blanco nominalmente:
$ find -type f -name file\*
./file
./file name
./directory/file
./directory/file name
Estoy tratando de inventar una versión segura de whispace de este scriptlet que tome la salida find
y la presente en select
:
$ select file in $(find -type f -name file); do echo $file; break; done
1) ./file
2) ./directory/file
Sin embargo, esto explota con espacios en blanco en los nombres de archivo:
$ select file in $(find -type f -name file\*); do echo $file; break; done
1) ./file 3) name 5) ./directory/file
2) ./file 4) ./directory/file 6) name
Por lo general, evitaría esto jugando IFS
. Sin embargo:
$ IFS=$'\n' select file in $(find -type f -name file\*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
$ IFS='\n' select file in $(find -type f -name file\*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
¿Cuál es la solución a esto?
bash
text-processing
whitespace
select
DopeGhoti
fuente
fuente
find
su capacidad para hacer coincidir un nombre de archivo en particular, simplemente puede usarselect file in **/file*
(después de la configuraciónshopt -s globstar
) enbash
4 o posterior.Respuestas:
Si solo necesita manejar espacios y pestañas (no líneas nuevas incrustadas), puede usar
mapfile
(o su sinónimoreadarray
) para leer en una matriz, por ejemplo, dadaluego
Si haces necesidad de nuevas líneas de mango, y su
bash
versión proporciona un nulo delimitado pormapfile
1 , entonces se puede modificar para queIFS= mapfile -t -d '' files < <(find . -type f -print0)
. De lo contrario, ensamble una matriz equivalente a partir de unafind
salida delimitada por nulos utilizando unread
bucle:1 la
-d
opción se añadió amapfile
enbash
la versión 4.4 IIRCfuente
mapfile
es nuevo para mí también. Prestigio.while IFS= read
versión funciona en bash v3 (que es importante para aquellos de nosotros que usamos macOS).find -print0
variante; refunfuñe por ponerlo después de una versión incorrecta conocida y describirlo solo para su uso si uno sabe que necesita manejar nuevas líneas. Si solo se maneja lo inesperado en lugares donde se espera, nunca se manejará lo inesperado en absoluto.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:O con los parámetros posicionales:
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:
O, si te gusta un bucle:
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:
Para seleccionar solo algunos archivos (como los que terminan en archivo) simplemente reemplace el *:
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:
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:
encontrar
Si debe usarse find por alguna razón, reemplace el delimitador con un NUL (0x00).
bash 4.4+
bash 2.05+
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:O, si lo que se necesita es un select (select es parte de bash, no sh):
[1] Este árbol (los \ 012 son líneas nuevas):
Podría construirse con estos dos comandos:
fuente
No puede establecer una variable frente a una construcción en bucle, pero puede establecerla frente a la condición. Aquí está el segmento de la página del manual:
(Un bucle no es un comando simple ).
Aquí hay una construcción de uso común que demuestra los escenarios de fracaso y éxito:
Desafortunadamente, no puedo ver una manera de incrustar un cambio
IFS
en laselect
construcción mientras afecta el procesamiento de un asociado$(...)
. Sin embargo, no hay nada que evite queIFS
se establezca fuera del ciclo:y es esta construcción con la que puedo ver que funciona
select
:Al escribir código defensiva Me gustaría recomendar que la cláusula o bien puede ejecutar en un subnivel, o
IFS
ySHELLOPTS
guardada y restaurada por la manzana:fuente
IFS=$'\n'
es seguro es infundado. Los nombres de archivo son perfectamente capaces de contener literales de nueva línea.[0-9a-f]{24}
. Se perdieron TB de copias de seguridad de los datos utilizados para respaldar la facturación del cliente.select
por su propio diseño es para soluciones escritas , por lo que siempre debe estar diseñado para manejar casos extremos.select
desde un shell donde está escribiendo los comandos para ejecutar, sino solo en un script, donde está respondiendo un mensaje proporcionado por ese script y dónde está ese script ejecutar una lógica predefinida (construida sin el conocimiento de los nombres de archivo en los que se opera) basada en esa entrada.Puede que esté fuera de mi jurisdicción aquí, pero tal vez puedas comenzar con algo como esto, al menos no tiene ningún problema con el espacio en blanco:
Para evitar posibles suposiciones falsas, como se señala en los comentarios, tenga en cuenta que el código anterior es equivalente a:
fuente
read -d
es una solución inteligente gracias por esto.read -d $'\000'
es exactamente idéntico aread -d ''
, pero para la gente engañosa acerca de las capacidades de bash (lo que implica, incorrectamente, que es capaz de representar NUL literales dentro de las cadenas). Ejecutes1=$'foo\000bar'; s2='foo'
y luego intente encontrar una manera de distinguir entre los dos valores. (Una versión futura puede normalizarse con el comportamiento de sustitución de comandos haciendo que el valor almacenado sea equivalente afoobar
, pero ese no es el caso hoy).