Tengo un script de shell bash que recorre todos los directorios secundarios (pero no los archivos) de un determinado directorio. El problema es que algunos de los nombres de directorio contienen espacios.
Aquí están los contenidos de mi directorio de prueba:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
Y el código que recorre los directorios:
for f in `find test/* -type d`; do
echo $f
done
Aquí está la salida:
prueba / Baltimore prueba / cereza colina prueba / Edison prueba / nuevo York Ciudad prueba / Filadelfia
Cherry Hill y la ciudad de Nueva York se tratan como 2 o 3 entradas separadas.
Intenté citar los nombres de archivo, así:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
pero fue en vano.
Tiene que haber una manera simple de hacer esto.
Las respuestas a continuación son geniales. Pero para hacer esto más complicado, no siempre quiero usar los directorios que figuran en mi directorio de prueba. A veces quiero pasar los nombres de directorio como parámetros de línea de comandos.
Tomé la sugerencia de Charles de establecer el IFS y se me ocurrió lo siguiente:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
y esto funciona bien a menos que haya espacios en los argumentos de la línea de comando (incluso si se citan esos argumentos). Por ejemplo, llamar al script de esta manera: test.sh "Cherry Hill" "New York City"
produce el siguiente resultado:
Cereza colina Nuevo York Ciudad
fuente
list="$@"
descarta por completo la lista de valores originales, colapsando en una cadena. Por favor, siga las prácticas en mi respuesta exactamente como se indica : tal asignación no se recomienda en ningún lugar; Si desea pasar una lista de argumentos de línea de comandos a un programa, debe recopilarlos en una matriz y expandir esa matriz directamente.Respuestas:
Primero, no lo hagas así. El mejor enfoque es usar
find -exec
adecuadamente:El otro enfoque seguro es usar la lista terminada en NUL, aunque esto requiere que encuentre soporte
-print0
:También puede llenar una matriz desde find y pasar esa matriz más tarde:
Si su búsqueda no es compatible
-print0
, su resultado no es seguro: lo siguiente no se comportará como se desea si existen archivos que contienen nuevas líneas en sus nombres (lo cual, sí, es legal):Si uno no va a usar uno de los anteriores, un tercer enfoque (menos eficiente en términos de tiempo y uso de memoria, ya que lee toda la salida del subproceso antes de dividir las palabras) es usar una
IFS
variable que no No contiene el carácter de espacio. Desactive globbing (set -f
) para evitar que las cadenas que contienen caracteres glob como[]
,*
o?
se expandan:Finalmente, para el caso de los parámetros de la línea de comandos, debería usar matrices si su shell los admite (es decir, es ksh, bash o zsh):
mantendrá la separación. Tenga en cuenta que la cita (y el uso de en
$@
lugar de$*
) es importante. Las matrices también se pueden completar de otras maneras, como las expresiones globales:fuente
Sin embargo, no funciona si el nombre del archivo contiene nuevas líneas. Lo anterior es la única solución que conozco cuando realmente desea tener el nombre del directorio en una variable. Si solo quieres ejecutar algún comando, usa xargs.
fuente
find . -type d | while read file; do ls "$file"; done
Aquí hay una solución simple que maneja pestañas y / o espacios en blanco en el nombre del archivo. Si tiene que lidiar con otros caracteres extraños en el nombre del archivo como líneas nuevas, elija otra respuesta.
El directorio de prueba
El código para ir a los directorios.
El nombre del archivo debe ser entrecomillado (
"$f"
) si se usa como argumento. Sin comillas, los espacios actúan como separador de argumentos y se dan múltiples argumentos al comando invocado.Y la salida:
fuente
alias duc='ls -d * | while read D; do du -sh "$D"; done;'
alias duc='du -sh *(/)'
-print0
yIFS='' read -r -d '' f
.Esto es extremadamente complicado en Unix estándar, y la mayoría de las soluciones funcionan con nuevas líneas o algún otro personaje. Sin embargo, si está utilizando el conjunto de herramientas GNU, puede explotar la
find
opción-print0
y utilizarlaxargs
con la opción correspondiente-0
(menos cero). Hay dos caracteres que no pueden aparecer en un nombre de archivo simple; esos son barra oblicua y NUL '\ 0'. Obviamente, la barra diagonal aparece en los nombres de ruta, por lo que la solución GNU de usar un NUL '\ 0' para marcar el final del nombre es ingeniosa y a prueba de tontos.fuente
¿Por qué no simplemente poner
en frente del comando? Esto cambia el separador de campo de <Espacio> <Pestaña> <Nueva línea> a solo <Nueva línea>
fuente
fuente
-d $'\0'
es exactamente lo mismo que-d ''
- porque bash usa cadenas terminadas en NUL, el primer carácter de una cadena vacía es un NUL, y por la misma razón, los NUL no pueden representarse dentro de las cadenas C en absoluto.yo suelo
¿No sería eso suficiente?
Idea tomada de http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
fuente
$(echo ...)
), no maneja correctamente los nombres de archivo con expresiones globales, no maneja correctamente los nombres de archivo que contienen$'\b'
o $ '\ n' caracteres, y además convierte varias ejecuciones de espacios en blanco en caracteres de espacios en blanco únicos en el lado de salida debido a una cita incorrecta.No almacene listas como cadenas; guárdelos como matrices para evitar toda esta confusión del delimitador. Aquí hay un script de ejemplo que funcionará en todos los subdirectorios de prueba o en la lista provista en su línea de comando:
Ahora intentemos esto en un directorio de prueba con una curva o dos:
fuente
"$@"
matriz y agregarla conset -- "$@" "$f"
.Puede usar IFS (separador de campo interno) temporalmente usando:
fuente
ps si solo se trata de espacio en la entrada, entonces algunas comillas dobles funcionaron sin problemas para mí ...
fuente
Para agregar a lo que dijo Jonathan : use la
-print0
opciónfind
junto conxargs
lo siguiente:Eso ejecutará el comando
command
con los argumentos adecuados; los directorios con espacios en ellos se citarán correctamente (es decir, se pasarán como un argumento).fuente
El código anterior convertirá los archivos .mov a .avi. Los archivos .mov están en diferentes carpetas y los nombres de las carpetas también tienen espacios en blanco . Mi script anterior convertirá los archivos .mov a archivos .avi en la misma carpeta. No sé si les ayuda a los pueblos.
Caso:
¡Salud!
fuente
echo "$name" | ...
no funciona siname
es así-n
, y cómo se comporta con nombres con secuencias de escape de barra diagonal inversa depende de su implementación: POSIX hace que el comportamientoecho
en ese caso sea explícitamente indefinido (mientras que POSIX extendido con XSI hace que la expansión de secuencias de escape de barra diagonal inversa sea un comportamiento definido de forma estándar y sistemas GNU - incluyendo fiesta - sinPOSIXLY_CORRECT=1
ruptura del estándar POSIX mediante la implementación-e
(mientras que la especificación requiereecho -e
para imprimir-e
.) en la salidaprintf '%s\n' "$name" | ...
es más seguro.También tenía que tratar con espacios en blanco en los nombres de ruta. Lo que finalmente hice fue usar una recursión y
for item in /path/*
:fuente
function
palabra clave: hace que su código sea incompatible con POSIX sh, pero no tiene otro propósito útil. Puede definir una función conrecursedir() {
, agregando los dos elementos parentales y eliminando la palabra clave de función, y esto será compatible con todos los shells compatibles con POSIX.Convierta la lista de archivos en una matriz Bash. Utiliza el enfoque de Matt McClure para devolver una matriz de una función Bash: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html El resultado es un camino para convertir cualquier entrada de varias líneas en una matriz Bash.
Este enfoque parece funcionar incluso cuando hay caracteres incorrectos, y es una forma general de convertir cualquier entrada a una matriz Bash. La desventaja es que si la entrada es larga, podría exceder los límites de tamaño de la línea de comandos de Bash o utilizar grandes cantidades de memoria.
Los enfoques en los que el ciclo que finalmente está funcionando en la lista también tiene la lista distribuida tienen la desventaja de que leer stdin no es fácil (como pedirle entrada al usuario), y el ciclo es un proceso nuevo, por lo que puede preguntarse por qué las variables que establezca dentro del bucle no estarán disponibles después de que finalice el bucle.
También no me gusta configurar IFS, puede estropear otro código.
fuente
IFS='' read
, en la misma línea, la configuración de IFS está presente solo para el comando de lectura y no se escapa. No hay razón para que no le guste configurar IFS de esta manera.Bueno, veo demasiadas respuestas complicadas. No quiero pasar la salida de la utilidad find o escribir un bucle, porque find tiene la opción "exec" para esto.
Mi problema era que quería mover todos los archivos con extensión dbf a la carpeta actual y algunos de ellos contenían espacios en blanco.
Lo abordé así:
Me parece muy simple
fuente
Acabo de descubrir que hay algunas similitudes entre mi pregunta y la tuya. Aparentemente si quieres pasar argumentos a comandos
para imprimirlos en orden
observe que $ @ está rodeado de comillas dobles, algunas notas aquí
fuente
Necesitaba el mismo concepto para comprimir secuencialmente varios directorios o archivos de una carpeta determinada. He resuelto usar awk para analizar la lista de ls y evitar el problema del espacio en blanco en el nombre.
¿Qué piensas?
fuente
fuente
Para mí esto funciona, y es bastante "limpio":
fuente
Acabo de tener un problema de variante simple ... Convertir archivos de .flv mecanografiado a .mp3 (bostezar).
encuentre de forma recursiva todos los archivos flash de usuario de Macintosh y conviértalos en audio (copia, sin transcodificación) ... es como en el momento anterior, señalando que la lectura en lugar de solo 'for file in
' se escapará.
fuente
read
despuésin
es una palabra más en la lista sobre la que estás iterando. Lo que has publicado es una versión ligeramente rota de lo que tenía el autor de la pregunta, que no funciona. Es posible que haya tenido la intención de publicar algo diferente, pero probablemente esté cubierto por otras respuestas aquí de todos modos.