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