Vous ne pouvez pas transformer le filtre grep a | grep c | grep -v d
en une simple commande grep
. Il n'y a que des moyens compliqués et inefficaces. Le résultat a une performance lente et le sens de l'expression est obscurci.
Combinaison de la trois greps en une seule commande
Si vous voulez simplement exécuter une seule commande, vous pouvez utiliser awk
qui fonctionne également avec des expressions régulières et peut les combiner avec des opérateurs logiques. Voici l'équivalent de votre filtre:
awk '/a/ && /c/ && $0 !~ /d/'
Je pense que dans la plupart des cas, il n'y a pas de raison de simplifier un pipe en une seule commande sauf lorsque la combinaison donne une expression grep relativement simple qui pourrait être plus rapide (voir résultats ci-dessous).
Les systèmes de type Unix sont conçus pour utiliser des pipes et pour connecter divers utilitaires ensemble. Bien que la communication par pipe ne soit pas la plus efficace possible, mais dans la plupart des cas, elle est suffisante. Parce qu'aujourd'hui la plupart des nouveaux ordinateurs ont des cœurs de CPU multiples, vous pouvez utiliser naturellement une parallélisation de CPU en utilisant simplement un pipe!
Votre filtre d'origine fonctionne très bien et je pense que dans de nombreux cas, la solution awk
serait un peu plus lente même sur un seul cœur.
Comparaison des performances
En utilisant un programme simple, j'ai généré un fichier de test aléatoire avec 200 000 000 lignes, chacune avec 4 caractères comme une combinaison aléatoire des caractères a
, b
, c
et d
. Le fichier fait 1 Go. Pendant les tests, il était complètement chargé en mémoire cache afin que les opérations sur le disque n'affectent pas la mesure des performances. Les tests ont été effectués sur un double cœur Intel.
Un seul grep
$ time ( grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$' testfile >/dev/null )
real 3m2.752s
user 3m2.411s
sys 0m0.252s
Un seul awk
$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real 0m54.088s
user 0m53.755s
sys 0m0.304s
Les trois greps originaux pipés
$ time ( grep a testfile | grep c | grep -v d >/dev/null )
real 0m28.794s
user 0m52.715s
sys 0m1.072s
Hybride - greps positifs combinés, négatif pipé
$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real 0m15.838s
user 0m24.998s
sys 0m0.676s
Ici, vous voyez que le seul grep
est très lent à cause de l'expression complexe. Le pipe original des trois greps est assez rapide en raison d'une bonne parallélisation. Sans parallélisation - sur un seul cœur - le pipe original s'exécute légèrement plus rapidement que awk
qui en tant que processus unique n'est pas parallélisé. Awk et grep utilisent probablement le même code d'expressions régulières et la logique des deux solutions est similaire.
Le grand gagnant est l'hybride qui combine deux greps positifs et laisse le négatif dans le pipe. Il semble que l'expression régulière avec |
n'ait pas de pénalité de performance.