Как выбрать наибольшее число из серии <string>_ # имен файлов в скрипте Bash - PullRequest
2 голосов
/ 27 августа 2010

У меня есть каталог с файлами

heat1.conf
heat2.conf
...
heat<n>.conf
minimize.conf
...
other files....

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

Какой лучший способ сделать это?

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

Ответы [ 3 ]

6 голосов
/ 27 августа 2010

Если вы собираетесь перечислить ваш файл только в текущем каталоге, тогда нет необходимости использовать find с maxdepth 1 или использовать ls.Просто используйте цикл for с расширением оболочки.Кроме того, expr является внешним.если ваш номер не содержит десятичных знаков, вы можете использовать только собственное сравнение bash.

max=-1
for file in heat*.conf
do
  num=${file:4}
  num=${file%.conf}
  [[ $num -gt $max ]] && max=$num    
done
echo "max is: $max"
3 голосов
/ 27 августа 2010

А как же:

max=$(find . -name 'heat[1-9]*.conf' -depth 1 |
      sed 's/heat\([0-9][0-9]*\)\.conf/\1/' |
      sort -n |
      tail -n 1)

Список возможных имен файлов; оставить только нечисловой бит; сортировать числа; выберите наибольшее (последнее) число.


Что касается скорости: не вдаваясь в язык сценариев, такой как Perl (Python, Ruby, ...), это близко к тому, что вы можете получить. Использование find вместо ls означает, что список имен файлов генерируется только один раз (первая версия этого ответа использовала ls, но это заставляет оболочку генерировать список имен файлов, а затем ls, чтобы повторить этот список). Команда sed довольно проста и генерирует список чисел, которые необходимо отсортировать. Можно утверждать, что сортировка в обратном числовом порядке (sort -nr), переданная в sed 1q, будет быстрее; второй sed будет читать меньше данных, и сортировка может не генерировать весь свой вывод до того, как SIGPIPE из sed закроет свой ввод (когда он завершится).

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

0 голосов
/ 27 августа 2010

Я придумала одно решение:

highest=-1
current_dir=`pwd`
cd $my_dir
for file in $(ls heat*) ; do #assume I've already checked for dir existence
    if [ "${file:4:$(($(expr length $file)-9))}" -gt "$highest" ]; then
    highest=${file:4:$(($(expr length $file)-9))}
    fi
done
cd $current_dir

.... Хорошо, я приняла ваши предложения и отредактировала свое решение, чтобы отменить выражение и предварительно сохранить переменную.В 1000 испытаниях мой метод (модифицированный) в среднем был быстрее, чем у Джона, но медленнее, чем у GhostDog, но стандартное отклонение было относительно большим.

Мой пересмотренный сценарий виден ниже в моем испытании, как и Джон и Ghost Dogрешения ...

declare -a timing

for trial in {1..1000}; do
    res1=$(date +%s.%N)
    highest=-1
    current_dir=`pwd`

    cd $my_dir
    for file in $(ls heat*) ; do 
        #assume I've already checked for dir existence
    file_no=${file:4:${#file}-9}
    if [ $file_no -gt $highest ]; then
        highest=$file_no
    fi
    done
    res2=$(date +%s.%N)
    timing[$trial]=$(echo "scale=9; $res2 - $res1"|bc)
    cd $current_dir
done

average=0
#compile net result
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    average=$( echo "scale=9; (($average+$current_entry/1000.0))"|bc)
done

std_dev=0
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    std_dev=$(echo "scale=9; (($std_dev + ($current_entry-$average)*($current_entry-$average)))"|bc)
done
std_dev=$(echo "scale=9; sqrt (($std_dev/1000))"|bc)
printf "Approach 1 (Jason), AVG Elapsed Time:    %.9F\n"  $average
printf "STD Deviation:                   %.9F\n"  $std_dev


for trial in {1..1000}; do
    res1=$(date +%s.%N)
    highest=-1
    current_dir=`pwd`

    cd $my_dir
    max=$(ls heat[1-9]*.conf |
    sed 's/heat\([0-9][0-9]*\)\.conf/\1/' |
    sort -n |
    tail -n 1)
    res2=$(date +%s.%N)
    timing[$trial]=$(echo "scale=9; $res2 - $res1"|bc)
    cd $current_dir
done

average=0
#compile net result
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    average=$( echo "scale=9; (($average+$current_entry/1000.0))"|bc)
done

std_dev=0
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    #echo "(($std_dev + ($current_entry-$average)*($current_entry-$average))"
    std_dev=$(echo "scale=9; (($std_dev + ($current_entry-$average)*($current_entry-$average)))"|bc)
done
std_dev=$(echo "scale=9; sqrt (($std_dev/1000))"|bc)
printf "Approach 2 (Jon), AVG Elapsed Time:    %.9F\n"  $average
printf "STD Deviation:                   %.9F\n"  $std_dev


for trial in {1..1000}; do
    res1=$(date +%s.%N)
    highest=-1
    current_dir=`pwd`

    cd $my_dir
    for file in heat*.conf
      do
      num=${file:4}
      num=${file%.conf}
      [[ $num -gt $max ]] && max=$num    
    done
    res2=$(date +%s.%N)
    timing[$trial]=$(echo "scale=9; $res2 - $res1"|bc)
    cd $current_dir
done

average=0
#compile net result
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    average=$( echo "scale=9; (($average+$current_entry/1000.0))"|bc)
done

std_dev=0
for trial in {1..1000}; do
    current_entry=${timing[$trial]}
    #echo "(($std_dev + ($current_entry-$average)*($current_entry-$average))"
    std_dev=$(echo "scale=9; (($std_dev + ($current_entry-$average)*($current_entry-$average)))"|bc)
done
std_dev=$(echo "scale=9; sqrt (($std_dev/1000))"|bc)
printf "Approach 3 (GhostDog), AVG Elapsed Time:    %.9F\n"  $average
printf "STD Deviation:                   %.9F\n"  $std_dev

... результаты:

Approach 1 (Jason), AVG Elapsed Time:    0.041418086
STD Deviation:                   0.177111854
Approach 2 (Jon), AVG Elapsed Time:    0.061025972
STD Deviation:                   0.212572411
Approach 3 (GhostDog), AVG Elapsed Time:    0.026292145
STD Deviation:                   0.145542801

Хорошая работа GhostDog ​​!!!И спасибо вам, Джон, и комментаторам за ваши советы!:)

...