2012-09-06 3 views
11

Ejemplo¿Cómo saber si el archivo en un bucle es el último?

for FILE in $DIR/* 
do 
    if(<is last File>) 
    doSomethingSpecial($FILE) 
    else 
    doSomethingRegular($FILE) 
    fi 
done 

Lo que hay que llamar para <is last file> para comprobar si el archivo actual es el último en la matriz?

¿Existe una verificación incorporada fácil sin verificar la longitud de la matriz por mi cuenta?

+0

No, no quieren procesar sólo el último archivo, pero para procesar cada archivo (como en mi ejemplo de bucle) y acaba de hacer otra operación en el último archivo –

+1

Disculpe si no estaba claro, puede usar ls + tail para detectar el último nombre de archivo. luego, en su bucle, compruebe el nombre del archivo -eq check. Esa sería la condición . No estoy antes de mi consola * nix, solo dando una idea – questzen

+0

'ls | tail' no funcionará si el último nombre de archivo contiene una nueva línea (una ocurrencia rara, pero no imposible). – chepner

Respuesta

8

No conozco ninguna forma de saber que está procesando el último elemento de una lista en un bucle for. Sin embargo se podría utilizar una matriz, iterar sobre todos, pero el último elemento, y luego procesar el último elemento fuera del bucle:

files=($DIR/*) 
for file in "${files[@]::${#files[@]}-1}" ; do 
    doSomethingRegular "$file" 
done 
doSomethingSpecial "${files[@]: -1:1}" 

La expansión ${files[@]:offset:length} evalúa a todos los elementos a partir de offset (o el principio si vacío) para length elementos. ${#files[@]}-1 es el número de elementos de la matriz menos 1.

${files[@]: -1:1} se evalúa como el último elemento - -1 desde el extremo, la longitud 1. El espacio es necesario ya que :- se trata de manera diferente a : -.

+2

Comenzando con 'bash' 4.x (no estoy seguro si es 4.0 o una versión de punto posterior), puede usar' $ {archivos [-1]} 'en lugar de' $ {archivos [@]: -1: 1} ' . – chepner

4

probar este

LAST_FILE="" 
for f in * 
do 
     if [ ! -z $LAST_FILE ] 
     then 
       echo "Process file normally $LAST_FILE" 
     fi 
     LAST_FILE=$f 
done 
if [ ! -z $LAST_FILE ] 
then 
     echo "Process file as last file $LAST_FILE" 
fi 

Produce

bash[1051]: ls 
1 2 3 4 
bash[1052]: sh ../last_file.sh 
Process file normally 1 
Process file normally 2 
Process file normally 3 
Process file as last file 4 
+0

Esto solo procesará el primer archivo normalmente. – chepner

+0

Incorrecto. He editado mi respuesta para mostrar la salida. –

+0

Disculpe, no leí su código con cuidado. Creo que eché de menos que no estabas trabajando directamente en '$ f', sino en' $ LAST_FILE'. – chepner

12

Lo que hay que llamar para comprobar si el archivo actual es el último en la matriz?

Para empezar, no está utilizando una matriz. Si se va, entonces sería fácil:

declare -a files 
files=($DIR/*) 
pos=$((${#files[*]} - 1)) 
last=${files[$pos]} 

for FILE in "${files[@]}" 
do 
    if [[ $FILE == $last ]] 
    then 
    echo "$FILE is the last" 
    break 
    else 
    echo "$FILE" 
    fi 
done 
+0

Agradecimiento usted, esta solución funciona bien. Pero estoy un poco decepcionado de que no haya una forma 'más agradable', sin los pasos de bajo nivel, para hacer esto. –

+0

Otros idiomas, p. Ej. Perl y Python, permiten la indexación desde la derecha con compensaciones negativas, por lo que la última es [-1]. Tal vez debería actualizar su idioma a algo más poderoso? ¿Por qué quieres hacerlo de todos modos? – cdarke

+1

En algún momento de la serie 4.x, 'bash' comenzó a permitir compensaciones de matriz negativas. – chepner

1

Usted puede abusar de los parámetros de posición, ya que actúan de manera similar a una matriz, pero son un poco más fáciles de manipular. Debería guardar los parámetros posicionales o ejecutarlos en una subshell.  

# Method 1: use a subshell. Slightly cleaner, but you can't always 
# do this (for example, you may need to affect variables in the current 
# shell 
files=($DIR/*) 

(
    set -- "${files[@]}" 
    until (($# == 1)); do 
     doSomethingRegular "$1" 
     shift 
    done  
    doSomethingSpecial "$1" 
) 

# Method 2: save the positional parameters. A bit uglier, but 
# executes everything in the same shell. 

files=($DIR/*) 
oldPP=("[email protected]") 
set -- "${files[@]}" 
until (($# == 1)); do 
    doSomethingRegular "$1" 
    shift 
done  
doSomethingSpecial "$1" 
set -- "${oldPP[@]}" 
2

Lo que hace que un archivo el último? ¿Hay algo especial al respecto? ¿Es el archivo con el mejor nombre cuando se ordena por nombre?

Quizás pueda tomar los nombres de archivo al revés. Entonces, es el primer archivo que desea tratar especial y no el último. descifrar la primera es una tarea mucho más fácil que hacer la última:

for file in $(ls -r1 $dir) 
do 
    if [ ! $processedLast ] 
    then 
     doSomethingSpecial($file) 
     processedLast=1 
    else 
     doSomethingRegular($file) 
    fi 
done 

No son necesarias las matrices. En realidad, me gusta la respuesta de chepner sobre el uso de parámetros posicionales.

2

Puede usar find para encontrar la cantidad total de archivos. Luego, cuando esté en el ciclo, cuente hasta el número total y realice su tarea cuando el total sea igual al recuento, es decir, el último archivo.

f=0 
tot_files=`find . -iname '*.txt' | wc -l` 
for FILE in $DIR/* 
do 
f=($f+1) 
if [[ $f == $tot_files ]];then 
carryout your task 
fi 
done 
1

Sobre la base de la actual respuesta más votado, desde @cdarke (https://stackoverflow.com/a/12298757/415523), si se mira un conjunto general de los valores (en lugar de archivos específicamente en el disco), el código del bucle sería la siguiente:

declare -a array 
declare -i length current 

array=(a b c d e c) 
length=${#array[@]} 
current=0 

for VALUE in "${array[@]}"; do 
    current=$((current + 1)) 

    if [[ "$current" -eq "$length" ]]; then 
    echo "$VALUE is the last" 
    else 
    echo "$VALUE" 
    fi 
done 

Esto produce la salida:

a 
b 
c 
d 
e 
c is the last 

Esto asegura que sólo el última elemento de la matriz activa la alternat Ive acción y que, si cualquier otro elemento en la matriz duplica el último valor, la acción alternativa es no llamado para los duplicados anteriores.

En el caso de una matriz de rutas a archivos en un directorio específico, p.

array=($DIR/*) 

... es probable que sea menos de una preocupación, ya que los nombres de archivos individuales dentro del mismo directorio son casi ciertamente único (a menos que tenga una muy sistema de archivos extraño!)

+0

Aún no es muy limpio, pero es una solución simple que funciona, incluso con arreglos no consecutivos (agujeros entre índices). Gracias. –

0

Es vieja pregunta - pero basándonos en la respuesta de @GregReynolds, utilice este one-liner si los comandos difieren solo por los parámetros en el último pase. Feo, código feo para los amantes de una sola línea

(ff="" ; for f in * "" ; do [ -n "$ff" ] && echo $(${f:+false} && echo $ff alternate params here || echo normal params $ff) ; ff=$f ; done) normal params 1 normal params 2 normal params 3 4 alternate params here

Cuestiones relacionadas