2010-08-26 3 views
2

J'ai un répertoire avec des fichiersComment sélectionner nombre le plus élevé de la série de <string> _ # noms de fichier dans Bash Script

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

Je veux que mon script Bash pour être en mesure de saisir le nom du fichier de nombre le plus élevé (donc je peux le supprimer et le remplacer quand je trouve une condition d'erreur).

Quelle est la meilleure façon d'y parvenir?

Veuillez discuter de la rapidité de votre solution et pourquoi vous pensez que c'est la meilleure approche.

+7

"S'il vous plaît discuter de la vitesse ..." ressemble à des devoirs. – msw

+1

HA! Non.Je suis juste curieux parce que je vais lancer ce script 24-7 dans le cadre d'un projet de recherche, donc je veux qu'il soit décemment rapide. Je ne suis même pas en CS - je suis un candidat au doctorat en génie chimique, bien que mes antécédents étaient en CE. Cet extrait de code fait partie d'un script bash beaucoup plus grand pour automatiser la soumission d'un ensemble de simulations de dynamique moléculaire chimique en plusieurs étapes et pour récupérer/diagnostiquer des simulations qui tombent en panne à la suite d'erreurs. –

+6

PS. Veuillez ne pas éditer les questions des personnes pour qu'elles soient marquées comme "devoirs" à moins qu'elles ne soient d'accord avec votre question. Mon message n'était pas fait à la maison et ça me déconcerte, vous avez édité ma question pour le dire. –

Répondre

6

Si vous ne voulez répertorier votre fichier que dans le répertoire courant, il n'est pas nécessaire d'utiliser find avec maxdepth 1 ou ls. Utilisez simplement une boucle for avec expansion de shell. En outre, expr est externe. Si votre nombre ne contient pas de décimales, vous pouvez utiliser la comparaison de 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

Qu'en est-:

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

Liste les noms de fichiers possibles; garde juste le bit non-numérique; trier les numéros; sélectionnez le plus grand (dernier) nombre.


En ce qui concerne la vitesse: sans tomber dans un langage de script comme Perl (Python, Ruby, ...), cela est loin d'être aussi bon que vous pouvez obtenir. L'utilisation de find au lieu de ls signifie que la liste des noms de fichiers est générée une seule fois (la première version de cette réponse utilise ls, mais cela provoque la génération par le shell de la liste des noms de fichiers, puis ls pour répercuter cette liste). La commande sed est assez simple et génère une liste de nombres à trier. Vous pourriez argumenter qu'une sorte dans l'ordre numérique inverse (sort -nr) canalisé dans sed 1q serait plus rapide; le second sed lirait moins de données, et le tri pourrait ne pas générer toute sa sortie avant que le SIGPIPE de sed ferme son entrée (comme il se termine).

Dans un langage de script tel que Perl, vous éviterez plusieurs processus et la surcharge de la communication entre ces processus. Ce serait plus rapide, mais il y aurait beaucoup moins de script shell impliqué.

0

je suis venu avec une solution:

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 

.... D'accord, je pris vos suggestions et ma solution modifié ferraille expr et pré-enregistrer la variable. En 1000 essais, ma méthode (modifiée) était en moyenne plus rapide que celle de Jon mais plus lente que celle de GhostDog, mais l'écart-type était relativement important.

Mon script révisé est vu ci-dessous dans mon procès, tout comme Jon et le fantôme des solutions de chiens ...

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 

... les résultats sont les suivants:

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 

Bonne ghostdog d'emploi !! ! Et merci à vous Jon et aux commentateurs pour vos conseils! :)

+0

Une amélioration évidente consiste à exécuter 'expr' une fois, pas deux fois, par nouveau numéro. 'expr' n'est pas un programme rapide. De même, dans un script shell dans son propre fichier, les commandes 'pwd' et final' cd' ne sont pas pertinentes; Ce n'est pas un fichier .bat DOS avec lequel vous avez affaire. Même en tant que fragment d'un script plus grand, j'utiliserais probablement un sous-shell, lui permettant de changer dans le répertoire cible tout en laissant le shell appelant exactement là où il était toujours. Vous pouvez éviter le 'ls 'qui fait simplement écho à la liste des fichiers que le shell a généré quand il a développé les jokers; vous pourriez rendre les jokers plus précis. –

+1

Puisque 'sh' n'a pas de sélection de sous-chaîne' $ {var: start: count} ', votre script est évidemment dans Bash. Il n'y a donc pas besoin d'utiliser 'expr'. De même, l'arithmétique est activée par défaut dans l'opérateur de sous-chaîne. Les comparaisons numériques doivent être faites dans '(())'. 'if (($ {file: 4: $ {# fichier} -9}> le plus haut))' –