bash itera la lista de archivos, excepto cuando está vacía

Pensé que esto sería simple, pero está resultando más complejo de lo que esperaba.

Quiero recorrer en iteración todos los archivos de un tipo particular en un directorio, así que escribo esto:

#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done 

Esto funciona siempre que haya al menos un archivo coincidente en el directorio. Sin embargo, si no hay archivos coincidentes, obtengo esto:

current file is *.zip 

Luego intenté:

#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done 

Si bien el cuerpo del bucle no se ejecuta cuando no hay archivos, obtengo un error de ls:

ls: *.zip: No such file or directory 

¿Cómo escribo un ¿bucle que no maneja limpiamente archivos coincidentes?

Comentarios

  • Agregue shopt -s nullglob antes de ejecutar el bucle for.
  • @cuolnglm: espeluznantemente, esto da como resultado que ls devuelva el nombre del script en ejecución en lugar de una lista vacía en este cuadro RHEL5 (bash 3.2.25) si lo hago FILES= ls * .zip ; for fname in "${FILES}"... pero funciona como se esperaba con for fname in *.zip ; do....
  • Use for file in *.zip, no `ls ...`. La sugerencia de @cuonglm ' es que *.zip se expanda a nada cuando el patrón no ' t coincide con cualquier archivo. ls sin argumentos enumera el directorio actual.
  • Esta pregunta explica por qué el análisis de la salida de ls generalmente debe ser evitado: ¿Por qué no analizar ls? ; también vea el enlace cerca de la parte superior de esa página al artículo de BashGuide ' s ParsingLs .
  • Posible duplicado de ¿Por qué mi script de shell se ahoga con espacios en blanco u otros caracteres especiales?

Responder

En bash, puede configurar la opción nullglob para que un patrón que coincide con nada «desaparece», en lugar de tratarse como una cadena literal:

shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done 

En el script de shell POSIX, simplemente verifica que fname existe (y al mismo tiempo con [ -f ], verifique que sea un archivo normal (o enlace simbólico a un archivo normal) y no de otro tipo como directorio / FIFO / dispositivo .. .):

for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done 

Reemplace [ -f "$fname" ] con [ -e "$fname" ] || [ -L "$fname ] si desea recorrer todos los archivos (no ocultos) cuyo nombre termina en .zip independientemente de su tipo.

Reemplace *.zip con .*.zip .zip *.zip si también desea considerar los archivos ocultos cuyo nombre termina en .zip.

Comentarios

  • shopt -s nullglob no funcionó para yo en Ubuntu 17.04, pero [ -f "$fname" ] || continue funcionó bien.
  • @koppor Parece que no ' t realmente está usando bash.
  • +1 para una solución POSIX.

Respuesta

set ./* #set the arg array to glob results ${2+":"} [ -e "$1" ] && #if more than one result skip the stat "$1" printf "current file is %s\n" "$@" #print the whole array at once ###or### ${2+":"} [ -e "$1" ] && #same kind of test for fname #iterate singly on $fname var for array do printf "file is %s\n" "$fname" #print each $fname for each iteration done 

En un comentario aquí mencionas invocar una función …

file_fn() if [ -e "$1" ] || #check if first argument exists [ -L "$1" ] #or else if it is at least a broken link then for f #if so iterate on "$f" do : something w/ "$f" done else command <"${1-/dev/null}" #only fail w/ error if at least one arg fi file_fn * 

Respuesta

Utilice buscar

export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \; 

DEBE exportar su función de shell con export -f para que esto funcione. Ahora find ejecuta bash que ejecuta su función de shell y permanece solo en el nivel de directorio actual.

Comentarios

  • Que se repite a través de subdirectorios, y quiero invocar una función bash (no un script) para las coincidencias.
  • @symcbean I ' he editado para limitar a un solo directorio y manejar funciones bash

Responder

En su lugar de:

FILES=`ls *.zip` 

Pruebe:

FILES=`ls * | grep *.zip` 

De esta manera, si ls falla (que hace en su caso) grep la salida fallida y regresará como una variable en blanco.

current file is <---Blank Here 

Puede agregar algo de lógica a esto para que devuelva «No Archivo encontrado «

#!/bin/bash FILES=`ls * | grep *.zip` if [[ $? == "0" ]]; then for fname in "$FILES" ; do echo current file is $fname done else echo "No Files Found" fi 

De esta manera, si el comando anterior tuvo éxito (salió con un valor 0), imprimirá el archivo actual; de lo contrario, imprimirá» No Archivos encontrados «

Comentarios

  • I thi nk es una mala idea agregar un proceso más (grep) en lugar de intentar solucionar el problema con una herramienta mejor (find ) o cambiando la configuración relevante para la solución actual (con shopt -s nullglob)
  • De acuerdo con el comentario de OP ' en su publicación original, shopt -s nullglob no funciona. Intenté find mientras verificaba mi respuesta y seguía fallando. Creo que por lo de exportación que dijo Dani.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *