2010-09-20 23 views
0

L'exemple suivant compare tous les fichiers dans un répertoire à la chaîne d'entrée ($ string) et retourner le nom de fichier correspondant. Ce n'est pas un moyen très élégant et efficace d'accomplir cela. À des fins de rapidité, j'ai modifié la condition for pour comparer uniquement aux fichiers commençant par le premier mot de $ string.Chaîne priorité de comparaison dans Bash

problème avec ce script suit - j'ai deux fichiers dans le répertoire:

Foo Bar.txt 
Foo Bar Foo.txt 

et je les compare à chaîne "Foo Bar 09.20.2010". Cela renverra les deux fichiers dans ce répertoire, car les deux fichiers correspondent. Mais je dois retourner seulement le fichier qui correspond à la chaîne de la manière la plus exacte - dans notre exemple, il devrait être Foo Bar.txt.

Aussi si vous avez de meilleures idées comment résoudre ce problème s'il vous plaît poster vos idées comme je ne suis pas encore compétent dans le script et je suis sûr qu'il y a des façons meilleures et peut-être même plus faciles de le faire.

#!/bin/bash 
string="Foo Bar 09.20.2010" 

for file in /path/to/directory/$(echo "$string" | awk '{print $1}')*; do 

    filename="${file##*/}" 
    filename="${filename%.*}" 


    if [[ $(echo "$string" | grep -i "^$filename") ]]; then 
     result="$file" 
     echo $result  
    fi 

done 

Voici rupture ce que je veux atteindre. Deux fichiers dans le répertoire pour correspondre à deux chaînes, Correct/Incorrect entre parenthèses signifie si le résultat était comme je l'ai prévu/voulu ou non.

2 fichiers dans le répertoire (enlevèrent extensions pour la correspondance):

Foo Bar.txt 
Foo Bar Foo.txt 

à comparer à 2 cordes:

Foo Bar Random Additional Text 
Foo Bar Foo Random Additional Text 

Résultats:

compare "Foo Bar"(.txt) against Foo Bar Random Additional Text -> Match (Correct) 
compare "Foo Bar"(.txt) against Foo Bar Foo Random Additional Text -> Match (Incorrect) 

compare "Foo Bar Foo"(.txt) against Foo Bar Random Additional Text -> NOT Match (Correct) 
compare "Foo Bar Foo"(.txt) against Foo Bar Foo Random Additional Text -> Match (Correct) 

Merci à tous pour votre réponses.

+0

@Andrew - donc s'il y a une correspondance exacte, vous voulez juste, mais quand il n'y a pas de correspondance exacte, une correspondance 'partielle' fera l'affaire? –

+0

@martin clayton Oui, une correspondance partielle est préférable, si aucune correspondance exacte n'est trouvée. – Gargauth

+0

D'accord, il semble que l'exaple que j'ai fourni n'était pas le meilleur. Laissez-moi essayer à nouveau - la chaîne 'Foo Bar 09.20.2010' devrait correspondre au fichier' Foo Bar.txt'. Et le fichier 'Foo Bar Foo 09.20.2010' devrait correspondre au fichier' Foo Bar Foo.txt'. J'espère que cela a plus de sens maintenant ... – Gargauth

Répondre

1

-moi si je me trompe, mais il semble que votre script est équivalent à:

ls /path/to/directory/"$string"* 

Si vous ne souhaitez un nom de fichier sur, vous pouvez utiliser head. Puisque ls répertorie les fichiers par ordre alphabétique, vous obtiendrez le premier par ordre alphabétique.

(Notez que lorsque la sortie de ls est canalisé vers un autre programme, il imprime un nom de fichier par ligne, ce qui rend plus facile à traiter que sa sortie sur la base de la colonne normale.)

ls /path/to/directory/"$string"* | head -1 

Pour le le plus court correspondance essayer quelque chose comme ce qui suit, qui utilise une combinaison maladroite de awk, sort -n et cut commander les lignes de la plus courte à la plus longue et puis imprimez le premier.

ls /path/to/directory/"$string"* | 
    awk '{print length($0) "\t" $0}' | sort -n | head -1 | cut -f 2- 
0

Beaucoup de vos echo et awk appels sont superflus. Pour obtenir tous les fichiers qui commencent par votre correspondance, vous pouvez simplement évaluer "$ string" *.

par exemple.à la fois

echo "$string"* 

et

ls "$string"* 

générerons vos listes. (Dans un tuyau, l'écho les séparera par espace, et ls les séparera par un saut de ligne). L'étape suivante consiste à réaliser que, étant donné que, comme vous l'avez défini, votre contrainte supplémentaire de "correspondance la plus exacte" est équivalente au nom de fichier correspondant le plus court.

Pour trouver la plus courte chaîne dans un ensemble de chaînes en bash (je préfère perl moi, mais collons avec la contrainte de le faire dans bash):

for fn in "/path/to/$string"*; do 
    echo $(echo $fn | wc -c) "$fn" 
done | sort -n | head -1 | cut -f2- -d' ' 

les boucles for boucle sur la noms de fichiers étendus. L'écho ajoute la longueur des noms aux noms. Ensuite, nous acheminons toute la sortie en sort -n et head -1 pour obtenir le nom le plus court, et cut -f2- -d' ' en enlève la longueur (en prenant le second champ avec un espace comme séparateur de champ).

La clé avec la programmation shell est de connaître vos blocs de construction et de les combiner. Avec des combinaisons intelligentes de tri, de tête, de queue et de coupe, vous pouvez faire beaucoup de traitement assez sophistiqué. Jetez en sed et uniq et vous êtes déjà capable de faire des choses assez impressionnantes. Cela étant dit, je n'utilise généralement la coque pour des choses comme ceci "à la volée" - pour tout ce que je pourrais vouloir réutiliser et qui est à tout complexe, je serais beaucoup plus susceptible de utilise perl.

+0

En fait, ça ne me dérange pas d'utiliser perl/python/php ou tout autre langage qui fera le travail. Je trouve juste que bash est un peu plus facile à utiliser, même si j'ajoute beaucoup de commandes superflues comme tu l'as dit :) – Gargauth