Optimiser sed pour les remplacements multiples

J’ai un fichier, users.txt , avec des mots comme,

 user1 user2 user3 

Je veux trouver ces mots dans un autre fichier, data.txt et y append un préfixe. data.txt a près de 500K lignes. Par exemple, user1 doit être remplacé par New_user1 et ainsi de suite. J’ai écrit un script shell simple comme

 for user in `cat users.txt` do sed -i 's/'${user}'/New_&/' data.txt done 

Pour environ 1000 mots, ce programme prend des minutes à traiter, ce qui m’a surpris car sed est très rapide quand il vient chercher et remplacer. J’ai essayé de faire référence à l’ option Optimiser le script shell pour plusieurs remplacements sed , mais les améliorations observées n’étaient pas encore très importantes.

Existe-t-il un autre moyen pour accélérer ce processus?

Vous pouvez transformer vos users.txt en commandes sed comme ceci:

 $ sed 's|.*|s/&/New_&/|' users.txt s/user1/New_user1/ s/user2/New_user2/ s/user3/New_user3/ 

Et ensuite, utilisez ceci pour traiter data.txt , soit en écrivant la sortie de la commande précédente dans un fichier intermédiaire, soit avec une substitution de processus:

 sed -f <(sed 's|.*|s/&/New_&/|' users.txt) data.txt 

Votre approche passe par tous les data.txt pour chaque ligne du users.txt , ce qui la ralentit.

Si vous ne pouvez pas utiliser la substitution de processus, vous pouvez utiliser

 sed 's|.*|s/&/New_&/|' users.txt | sed -f - data.txt 

au lieu.

Sed est connu pour être très rapide (probablement seulement pire que C).

Au lieu de sed 's/X/Y/g' input.txt , essayez sed '/X/ s/X/Y/g' input.txt . Ce dernier est connu pour être plus rapide.

Comme vous ne disposez que d’une “sémantique à la fois”, vous pouvez l’exécuter en parallel (sur des processeurs multi-core) comme ceci:

 cat huge-file.txt | parallel --pipe sed -e '/xxx/ s/xxx/yyy/g' 

Si vous travaillez avec des fichiers ASCII simples, vous pouvez les accélérer en utilisant les parameters régionaux “C”:

 LC_ALL=C sed -i -e '/xxx/ s/xxx/yyy/g' huge-file.txt 

Ou .. en une fois, nous pouvons faire quelque chose comme ça. Disons, nous avons un fichier de données avec 500k lignes.

 $> wc -l data.txt 500001 data.txt $> ls -lrtha data.txt -rw-rw-r--. 1 gaurav gaurav 16M Oct 5 00:25 data.txt $> head -2 data.txt ; echo ; tail -2 data.txt 0|This is a test file maybe 1|This is a test file maybe 499999|This is a test file maybe 500000|This is a test file maybe 

Disons que notre users.txt a 3-4 mots-clés, avec comme préfixe “ab_”, dans le fichier “data.txt”

 $> cat users.txt file maybe test 

Nous voulons donc lire users.txt et pour chaque mot, nous voulons changer ce mot en un nouveau mot. Par exemple, “fichier” pour “ab_file”, “peut-être” pour “ab_maybe” ..

Nous pouvons exécuter une boucle while, lire les mots d’entrée à préfixer un par un, puis exécuter une commande perl sur le fichier avec le mot d’entrée stocké dans une variable. Dans l’exemple ci-dessous, le mot de lecture est transmis à la commande perl en tant que $ word.

J’ai chronométré cette tâche et cela se passe assez rapidement. Est-ce que c’était sur ma VM hébergée sur Windows 10 (en utilisant Centos7)?

 time cat users.txt |while read word; do perl -pi -e "s/${word}/ab_${word}/g" data.txt; done real 0m1.973s user 0m1.846s sys 0m0.127s $> head -2 data.txt ; echo ; tail -2 data.txt 0|This is a ab_test ab_file ab_maybe 1|This is a ab_test ab_file ab_maybe 499999|This is a ab_test ab_file ab_maybe 500000|This is a ab_test ab_file ab_maybe 

Dans le code ci-dessus, nous lisons les mots: test, file, et peut-être changeons-le en ab_test, ab_file, ab_maybe dans le fichier data.txt. le compte de tête et de queue confirme notre opération.

acclamations, Gaurav