2010-12-06 2 views
2

m'a donné ce fichier texte, appelez stock.txt, le contenu du fichier texte est le suivant:Manipulation du fichier de texte de données avec la commande bash?

pepsi;drinks;3 
fries;snacks;6 
apple;fruits;9 
baron;drinks;7 
orange;fruits;2 
chips;snacks;8 

je vais devoir utiliser pour venir bash script cette sortie:

Total amount for drinks: 10 
Total amount for snacks: 14 
Total amount for fruits: 11 
Total of everything: 35 

Mon instinct me dit que je vais devoir utiliser sed, group, grep et autre chose.
Par où commencer?

+0

Merci d'être honnête. Ouais, il est tentant d'obtenir la solution directe, mais vous serez foutu du temps d'examen si nous le faisons pour vous. Mais les gens ici sont heureux de vous donner quelques idées. –

+0

Salut Rafe, c'est ce que je suis inquiet .. Examen .. hhaha .. Donc je dois être honnête, et trouver la solution par moi-même, mais j'ai besoin de vos directives, les notes de cours que j'ai sont très limitées, manque d'exemples . – bashington02

+1

Existe-t-il des restrictions sur les outils que vous êtes autorisé à utiliser? awk pourrait le faire assez facilement ... –

Répondre

0

Je briser l'exercice en étapes

Étape 1: Lire le fichier une ligne à la fois

while read -r line 
do 
    # do something with $line 
done 

Étape 2: match Motif (boissons, snacks, fruits) et faire quelques simples arithmétique. Cette étape nécessite que vous marquez chaque ligne que je vais laisser un exercice pour vous de comprendre.

if [[ "$line" =~ "drinks" ]] 
then 
    echo "matched drinks" 
    . 
    . 
    . 
fi 
+0

Merci Amir, j'utilise la déclaration while et if comme vous l'avez mentionné, et ça marche maintenant. Mais je suis plus curieux de savoir comment utiliser sed/grep/awk pour y parvenir. Des guides pour moi? – bashington02

0

Il y a une courte description ici sur le traitement des fichiers séparés par des virgules bash ici:

http://www.cyberciti.biz/faq/unix-linux-bash-read-comma-separated-cvsfile/

Vous pourriez faire quelque chose de similaire. Il suffit de changer IFS de la virgule au point-virgule.

Oh oui, et un conseil général pour apprendre bash: man est votre ami. Utilisez cette commande pour voir les pages de manuel pour toutes (ou la plupart) des commandes et des utilitaires.

Exemple: man read montre la page de manuel pour la commande de lecture. Sur la plupart des systèmes, il sera ouvert en less, vous devez donc quitter le manuel en appuyant sur q (peut-être drôle, mais cela m'a pris du temps pour le comprendre)

+0

Merci pour le lien. Je le lis =) Mais l'awk très tentant .. il semble très facile de résoudre ma question. http://lowfatlinux.com/linux-awk.html#DATA. BUt comme, KeinDTimm dit, je devrais utiliser sed/grep d'abord .. En tout cas, je vais explorer 1 par 1 – bashington02

+0

awk est en effet beaucoup plus cool que cela. Eh bien, le meilleur conseil que je pourrais vous donner est: faire les deux: D –

+0

Salut Goran, j'arrive à le faire mais en utilisant while et simple if statements. Je n'ai toujours pas de contact avec le sed/grep et .... l'AWK ... un indice sur l'utilisation de ces trois commandes? – bashington02

1

Pure Bash. Une application agréable pour un tableau associatif:

declare -A category     # associative array 
IFS=';' 
while read name cate price ; do 
    ((category[$cate]+=price)) 
done < stock.txt 

sum=0 
for cate in ${!category[@]}; do  # loop over the indices 
    printf "Total amount of %s: %d\n" $cate ${category[$cate]} 
    ((sum+=${category[$cate]})) 
done 

printf "Total amount of everything: %d\n" $sum 
+1

Pure Bash ** 4 **. –

0

La façon facile de le faire est d'utiliser une table de hachage, qui est directement pris en charge par bash 4.x et bien sûr se trouve dans awk et perl. Si vous n'avez pas de table de hachage, vous devez faire une boucle deux fois: une fois pour collecter les valeurs uniques de la deuxième colonne, une fois pour totaliser.

Il y a plusieurs façons de le faire. En voici une amusante qui n'utilise pas awk, sed ou perl. Les seuls utilitaires externes que j'ai utilisés ici sont cut, sort et uniq. Vous pouvez même remplacer cut avec un peu plus d'effort. En fait les lignes 5-9 auraient pu être écrites plus facilement avec grep, (grep $kind stock.txt) mais j'ai évité cela pour montrer la puissance de bash.

for kind in $(cut -d\; -f 2 stock.txt | sort | uniq) ; do 
    total=0 
    while read d ; do 
     total=$((total+d)) 
    done < <(
     while read line ; do 
      [[ $line =~ $kind ]] && echo $line 
     done < stock.txt | cut -d\; -f3 
    ) 

    echo "Total amount for $kind: $total" 
done 

Nous perdons la commande stricte de votre sortie d'origine ici. Un exercice pour vous pourrait être de trouver un moyen de ne pas le faire.

Discussion: La première ligne décrit un sous-shell avec un pipeline simple utilisant cut. Nous lisons le troisième champ du fichier stock.txt, avec des champs délimités par ;, écrit \; ici, donc le shell ne l'interprète pas. Le résultat est une liste de valeurs séparées par un saut de ligne de stock.txt.Ceci est canalisé à sort, puis uniq. Ceci effectue notre étape de "regroupement", puisque le pipeline affichera une liste alphabétique des éléments de la deuxième colonne mais ne listera chaque élément qu'une fois, peu importe le nombre de fois qu'il est apparu dans le fichier d'entrée.

Sur la première ligne se trouve également une boucle for typique: Pour chaque élément résultant de la sous-coque, nous bouclons une fois, en stockant la valeur de l'article dans la variable kind. C'est l'autre moitié de l'étape de regroupement, en s'assurant que chaque ligne de sortie "Total" se produit une fois.

Sur la deuxième ligne total est initialisée à zéro afin qu'elle soit réinitialisée chaque fois qu'un nouveau groupe est démarré.

La troisième ligne commence la boucle 'totalisation', dans laquelle pour la kind actuelle, nous trouvons la somme de ses occurrences. nous déclarons ici que nous allons lire la variable d depuis stdin à chaque itération de la boucle.

Sur la quatrième ligne du montant total se produit en fait: L'utilisation shell arithmatic on ajoute la valeur d à la valeur total.

La ligne cinq termine la boucle while, puis décrit son entrée. Nous utilisons la redirection d'entrée shell via < pour spécifier que l'entrée de la boucle, et donc la commande read, provient d'un fichier. Nous utilisons ensuite process substitution pour spécifier que le fichier sera réellement le résultat d'une commande.

Sur la sixième ligne, la commande qui alimentera la boucle de lecture continue commence. Il est lui-même une autre boucle while-read, cette fois en lisant dans la variable line. Sur la septième ligne, le test est effectué via un conditional construct. Ici, nous utilisons [[ pour son opérateur =~, qui est un opérateur de correspondance de modèle. Nous testons pour voir si $line correspond à notre $kind actuel.

Sur la huitième ligne on finit la boucle while lecture interne et précisons que son entrée provient du fichier stock.txt, puis on conduit la sortie de la boucle entière, ce qui en est maintenant simplement toutes les lignes correspondant à $kind, à cut et ordonnez-lui de n'afficher que le troisième champ, qui est le champ numérique. Sur la ligne 9, nous terminons alors la commande de substitution de processus, dont la sortie est une liste de nombres délimités par une nouvelle ligne à partir des lignes qui faisaient partie du groupe spécifié par kind. Etant donné que le total est maintenant connu et que le type est connu, il est simple d'imprimer les résultats à l'écran.

0

La réponse ci-dessous est OP. Comme il a été édité dans la question elle-même et OP n'est pas revenu depuis 6 ans, j'édite la réponse de la question et l'affiche comme wiki ici.


Ma réponse, pour obtenir le prix total, j'utilise ceci:

... 
PRICE=0 
IFS=";"  # new field separator, the end of line 
while read name cate price 
do 
let PRICE=PRICE+$price 
done < stock.txt 
echo $PRICE 

Quand je fais l'écho, son: 35, ce qui est correct. Maintenant, je vais continuer à utiliser awk pour obtenir le résultat de la sous-catégorie.

solution complète:

Merci les gars, je parviens à le faire moi-même.Voici mon code:

#!/bin/bash 
INPUT=stock.txt 
PRICE=0 
DRINKS=0 
SNACKS=0 
FRUITS=0 
old_IFS=$IFS  # save the field separator 
IFS=";"  # new field separator, the end of line 
while read name cate price 
do 
    if [ $cate = "drinks" ]; then 
     let DRINKS=DRINKS+$price 
fi 

if [ $cate = "snacks" ]; then 
     let SNACKS=SNACKS+$price 
fi 

if [ $cate = "fruits" ]; then 
     let FRUITS=FRUITS+$price 
fi 

# Total 
let PRICE=PRICE+$price 
done < $INPUT 

echo -e "Drinks: " $DRINKS 
echo -e "Snacks: " $SNACKS 
echo -e "Fruits: " $FRUITS 
echo -e "Price " $PRICE 
IFS=$old_IFS 
Questions connexes