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
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 (conshopt -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.
shopt -s nullglob
antes de ejecutar el bucle for.FILES=
ls * .zip; for fname in "${FILES}"...
pero funciona como se esperaba confor fname in *.zip ; do....
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.ls
generalmente debe ser evitado: ¿Por qué no analizarls
? ; también vea el enlace cerca de la parte superior de esa página al artículo de BashGuide ' s ParsingLs .