Как узнать, является ли файл в цикле последним?

Пример

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

Как вызвать <is last file>, чтобы проверить, является ли текущий файл последним в массиве?

Есть ли простая встроенная проверка без проверки длины массива самостоятельно?

Ответ 1

Что нужно, чтобы проверить, является ли текущий файл последним в массиве?

Для начала вы не используете массив. Если бы вы были тогда, это было бы легко:

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 

Ответ 2

Я знаю, что не могу сказать, что вы обрабатываете последний элемент списка в цикле for. Однако вы можете использовать массив, перебирать все, кроме последнего элемента, а затем обрабатывать последний элемент вне цикла:

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

Расширение ${files[@]:offset:length} оценивает все элементы, начинающиеся с offset (или начало, если пусто) для элементов length. ${#files[@]}-1 - количество элементов в массиве минус 1.

${files[@]: -1:1} оценивает последний элемент - -1 с конца, длина 1. Пространство необходимо, так как :- обрабатывается по-разному с : -.

Ответ 3

Попробуйте это

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

Выдает

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

Ответ 4

Что делает файл последним? Есть ли что-то особенное? Это файл с наибольшим именем при сортировке по имени?

Возможно, вы можете взять имена файлов назад. Затем, это первый файл, который вы хотите обработать специальным, а не последним. вычисление первой задачи намного проще, чем выполнение последнего:

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

Никаких массивов не требуется. На самом деле, мне нравится chepner ответить об использовании позиционных параметров.

Ответ 5

Вы можете использовать find, чтобы найти общее количество файлов.  Затем, когда вы находитесь в цикле, подсчитываете общее число и выполняете свою задачу, когда сумма равна счету i.e, последнему файлу.

 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

Ответ 6

Вы можете злоупотреблять позиционными параметрами, поскольку они действуют аналогично массиву, но их немного легче манипулировать. Вы должны либо сохранить старый позиционный параметры или выполнить в подоболочке.  

# 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[@]}"

Ответ 7

Основываясь на текущем наивысшем голосовании от @cdarke (fooobar.com/questions/482501/...), если смотреть на общий массив значений (а не на определенные файлы на диске), код цикла будет выглядеть следующим образом:

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

Это дает результат:

a
b
c
d
e
c is the last

Это гарантирует, что только последний элемент в массиве вызывает альтернативное действие и что, если какой-либо другой элемент в массиве дублирует последнее значение, альтернативное действие не вызывается для более ранних дубликатов.

В случае массива путей к файлам в определенном каталоге, например

array=( $DIR/* )

... это, вероятно, меньше беспокоит, так как отдельные имена файлов в одном каталоге почти наверняка уникальны (если у вас нет действительно нечетной файловой системы!)

Ответ 8

Старый вопрос - но, основываясь на ответе от @GregReynolds, используйте этот однострочный ключ, если команды отличаются только параметрами последнего прохода. Уродливый, уродливый код для любителей одного лайнера

( 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