13 outils de traitement de texte basés sur le shell

Voici un extrait d'un futur livre, Basic Tools and Practices for a Novice Software Developer, par Balthazar Ruberol et Etienne Broad . Le livre devrait aider à éduquer la jeune génération de développeurs. Il couvrira des sujets tels que la maîtrise de la console, la mise en place et de travailler efficacement dans l'interpréteur de commandes, la gestion des versions de code à l' aide des gitfondamentaux de SQL, des outils tels que Make, jqet des expressions régulières, bases de la mise en réseau, ainsi que les meilleures pratiques pour le développement de logiciels et de collaboration. Les auteurs travaillent actuellement dur sur ce projet et invitent tout le monde à participer à la liste de diffusion .

Contenu



Traitement de texte Shell


L'une des raisons qui font du shell de commandes un outil précieux est le grand nombre de commandes de traitement de texte et la possibilité de les combiner facilement dans le pipeline, créant des modèles de traitement complexes. Ces commandes rendent de nombreuses tâches triviales pour l'analyse de texte et de données, la conversion de données entre différents formats, le filtrage de chaînes, etc.

Lorsque vous travaillez avec des données de texte, le principe principal est de décomposer tout problème complexe en de nombreux plus petits - et de résoudre chacun d'entre eux à l'aide d'un outil spécialisé.

Faire en sorte que chaque programme fasse bien une chose - Les fondements de la philosophie Unix

Les exemples de ce chapitre peuvent sembler un peu farfelus à première vue, mais cela est fait exprès. Chacun des outils est conçu pour résoudre un petit problème. Cependant, lorsqu'ils sont combinés, ils deviennent extrêmement puissants.

Nous allons examiner certaines des commandes de traitement de texte les plus courantes et les plus utiles dans le shell de commandes et montrer les flux de travail réels qui les connectent ensemble. Je suggère de regarder le mana de ces équipes pour voir toute l'étendue des possibilités à votre disposition.

Un exemple de fichier CSV est disponible en ligne . Vous pouvez le télécharger pour vérifier le matériel.

chat


La commande est catutilisée pour compiler une liste d'un ou plusieurs fichiers et afficher leur contenu à l'écran.

$ cat Documents/readme
Thanks again for reading this book!
I hope you're following so far!

$ cat Documents/computers
Computers are not intelligent
They're just fast at making dumb things.

$ cat Documents/readme Documents/computers
Thanks again for reading this book!
I hope you are following so far!

Computers are not intelligent
They're just fast at making dumb things.

tête


headimprime les n premières lignes du fichier. Cela peut être très utile pour rechercher un fichier de structure et de format inconnus sans remplir la console entière avec un tas de texte.

$ head -n 2 metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name
mysql.galera.wsrep_cluster_size,gauge,,node,,The current number of nodes in the Galera cluster.,0,mysql,galera cluster size

S'il -nn'est pas spécifié, headimprime les dix premières lignes du fichier ou du flux d'entrée spécifié.

queue


tail- un analogue head, seulement il affiche les n dernières lignes du fichier.

$ tail -n 1 metadata.csv
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

Si vous souhaitez imprimer toutes les lignes situées après la nième ligne (y compris celle-ci), vous pouvez utiliser l'argument -n +n.

$ tail -n +42 metadata.csv
mysql.replication.slaves_connected,gauge,,,,Number of slaves connected to a replication master.,0,mysql,slaves connected
mysql.performance.queries,gauge,,query,second,The rate of queries.,0,mysql,queries

Il y a 43 lignes dans notre fichier, donc il tail -n +42n'en sort que les 42e et 43e lignes.

Si le paramètre n'est -npas spécifié, il tailaffichera les dix dernières lignes du fichier ou du flux d'entrée spécifié.

tail -fou tail --followafficher les dernières lignes du fichier et chaque nouvelle ligne au fur et à mesure qu'elles sont écrites dans le fichier. Ceci est très utile pour visualiser l'activité en temps réel, par exemple, ce qui est enregistré dans les journaux du serveur Web, etc.

toilettes


wc(nombre de mots) affiche le nombre de caractères ( -c), de mots ( -w) ou de lignes ( -l) dans le fichier ou le flux spécifié.

$ wc -l metadata.csv
43  metadata.csv
$ wc -w metadata.csv
405 metadata.csv
$ wc -c metadata.csv
5094 metadata.csv

Par défaut, tout ce qui précède est affiché.

$ wc metadata.csv
43     405    5094 metadata.csv

Si les données de texte sont redirigées ou redirigées vers stdin, seul le compteur est affiché.

$ cat metadata.csv | wc
43     405    5094
$ cat metadata.csv | wc -l
43
$ wc -w < metadata.csv
405

grep


grep- Il s'agit d'un couteau suisse filtrant les cordes selon un schéma donné.

Par exemple, nous pouvons trouver toutes les occurrences du mot mutex dans un fichier.

$ grep mutex metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.mutex_spin_rounds,gauge,,event,second,The rate of mutex spin rounds.,0,mysql,mutex spin rounds
mysql.innodb.mutex_spin_waits,gauge,,event,second,The rate of mutex spin waits.,0,mysql,mutex spin waits

greppeut traiter soit les fichiers spécifiés comme arguments, soit un flux de texte qui lui est transmis stdin. Ainsi, nous pouvons concaténer plusieurs commandes greppour filtrer davantage le texte. Dans l'exemple suivant, nous filtrons les lignes de notre fichier metadata.csvpour rechercher des lignes contenant à la fois mutex et OS .

$ grep mutex metadata.csv | grep OS
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits

Examinons quelques options grepet leur comportement.

grep -vEffectue une correspondance inverse: filtre les chaînes qui ne correspondent pas au modèle d'argument.

$ grep -v gauge metadata.csv
metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name

grep -iEffectue une correspondance insensible à la casse. L'exemple suivant grep -i ostrouve à la fois OS et OS .

$ grep -i os metadata.csv
mysql.innodb.mutex_os_waits,gauge,,event,second,The rate of mutex OS waits.,0,mysql,mutex os waits
mysql.innodb.os_log_fsyncs,gauge,,write,second,The rate of fsync writes to the log file.,0,mysql,log fsyncs

grep -l Répertorie les fichiers contenant une correspondance.

$ grep -l mysql metadata.csv
metadata.csv

L'équipe grep -ccompte combien de fois un échantillon a été trouvé.

$ grep -c select metadata.csv
3

grep -r recherche récursivement des fichiers dans le répertoire de travail actuel et tous ses sous-répertoires.

$ grep -r are ~/Documents
/home/br/Documents/computers:Computers are not intelligent
/home/br/Documents/readme:I hope you are following so far!

grep -w affiche uniquement les mots entiers correspondants.

$ grep follow ~/Documents/readme
I hope you are following so far!
$ grep -w follow ~/Documents/readme
$

Couper


cutextrait une partie du fichier (ou, comme d'habitude, le flux d'entrée). La commande définit le séparateur de champ (qui sépare les colonnes) à l'aide de l'option -det les numéros de colonne à récupérer à l'aide de l'option -f.

Par exemple, la commande suivante récupère la première colonne des cinq dernières lignes de notre fichier CSV.

$ tail -n 5 metadata.csv | cut -d , -f 1
mysql.performance.user_time
mysql.replication.seconds_behind_master
mysql.replication.slave_running
mysql.replication.slaves_connected
mysql.performance.queries

Comme nous traitons avec CSV, les colonnes sont séparées par une virgule, et l'option est responsable de la récupération de la première colonne -f 1.

Vous pouvez sélectionner les première et deuxième colonnes à l'aide de l'option -f 1,2.

$ tail -n 5 metadata.csv | cut -d , -f 1,2
mysql.performance.user_time,gauge
mysql.replication.seconds_behind_master,gauge
mysql.replication.slave_running,gauge
mysql.replication.slaves_connected,gauge
mysql.performance.queries,gauge

pâte


paste fusionne deux fichiers différents en un seul fichier multi-colonnes.

$ cat ingredients
eggs
milk
butter
tomatoes
$ cat prices
1$
1.99$
1.50$
2$/kg
$ paste ingredients prices
eggs    1$
milk    1.99$
butter  1.50$
tomatoes    2$/kg

Par défaut, il pasteutilise un délimiteur de tabulation, mais il peut être modifié à l'aide du paramètre -d.

$ paste ingredients prices -d:
eggs:1$
milk:1.99$
butter:1.50$
tomatoes:2$/kg

Un autre cas d'utilisation courant paste consiste à combiner toutes les lignes d'un flux ou d'un fichier à l'aide du délimiteur spécifié, à l'aide d'une combinaison de -set -d.

$ paste -s -d, ingredients
eggs,milk,butter,tomatoes

Si un paramètre est spécifié en tant que fichier d'entrée -, il sera lu à la place stdin.

$ cat ingredients | paste -s -d, -
eggs,milk,butter,tomatoes

Trier


La commande sorttrie en fait les données (dans le fichier ou le flux d'entrée spécifié).

$ cat ingredients
eggs
milk
butter
tomatoes
salt
$ sort ingredients
butter
eggs
milk
salt
tomatoes

sort -r effectue un tri inverse.

$ sort -r ingredients
tomatoes
salt
milk
eggs
butter

sort -n Trie les champs par leur valeur arithmétique.

$ cat numbers
0
2
1
10
3
$ sort numbers
0
1
10
2
3
$ sort -n numbers
0
1
2
3
10

uniq


uniq Détecte et filtre les lignes identiques adjacentes dans le fichier ou le flux d'entrée spécifié.

$ cat duplicates
and one
and one
and two
and one
and two
and one, two, three
$ uniq duplicates
and one
and two
and one
and two
and one, two, three

Puisqu'il ne uniqfiltre que les lignes adjacentes , des doublons peuvent toujours rester dans nos données. Pour filtrer toutes les mêmes lignes d'un fichier, vous devez d'abord trier son contenu.

$ sort duplicates | uniq
and one
and one, two, three
and two

uniq -c au début de chaque ligne insère le nombre de ses occurrences.

$ sort duplicates | uniq -c
   3 and one
   1 and one, two, three
   2 and two

uniq -u Affiche uniquement des chaînes uniques.

$ sort duplicates | uniq -u
and one, two, three

Remarque. uniqIl est particulièrement utile en combinaison avec le tri, car le pipeline | sort | uniqvous permet de supprimer toutes les lignes en double dans un fichier ou un flux.

awk


awk- C'est un peu plus qu'un simple outil de traitement de texte: en fait, il a tout un langage de programmation . Ce qui est awk vraiment bien, c'est de diviser les fichiers en colonnes, et il le fait avec une brillance particulière lorsque les espaces et les tabulations sont mélangés dans des fichiers.

$ cat -t multi-columns
John Smith    Doctor^ITardis
Sarah-James Smith^I    Companion^ILondon
Rose Tyler   Companion^ILondon

Remarque. cat -taffiche les onglets comme ^I.

Comme vous pouvez le voir, les colonnes sont séparées par des espaces ou des tabulations, et pas toujours par le même nombre d'espaces. cutil est inutile ici car il fonctionne avec un seul caractère séparateur. Mais awkil est facile de gérer un tel fichier.

awk '{ print $n }'affiche la nième colonne dans le texte.

$ cat multi-columns | awk '{ print $1 }'
John
Sarah-James
Rose
$ cat multi-columns | awk '{ print $3 }'
Doctor
Companion
Companion
$ cat multi-columns | awk '{ print $1,$2 }'
John Smith
Sarah-James Smith
Rose Tyler

Bien que awkcapable de bien plus, la sortie des haut-parleurs représente probablement 99% des cas d'utilisation dans mon cas personnel.

Remarque. { print $NF }affiche la dernière colonne d'une ligne.

tr


trsignifie traduire . Cette commande remplace un caractère par un autre. Il fonctionne avec des caractères ou des classes de caractères tels que les minuscules, les caractères typés, les espaces, les caractères alphanumériques, etc.

En entrée standard, il tr <char1> <char2>remplace toutes les occurrences de <char1> par <char2>.

$ echo "Computers are fast" | tr a A
computers Are fAst

trpeut traduire des classes de caractères en utilisant la notation [:class:]. Une liste complète des classes disponibles est décrite sur la page de manuel tr, mais certaines sont présentées ici.

[:space:]représente tous les types d'espaces, des simples espaces aux tabulations ou aux nouvelles lignes.

$ echo "computers are fast" | tr '[:space:]' ','
computers,are,fast,%

Tous les caractères, comme les espaces, sont séparés par des virgules. Veuillez noter que le caractère %à la fin de la sortie indique l'absence d'une nouvelle ligne de fin. En effet, ce personnage est également converti en virgule.

[:lower:]représente tous les caractères minuscules et [:upper:] tous les caractères majuscules. Ainsi, la transformation entre eux devient triviale.

$ echo "computers are fast" | tr '[:lower:]' '[:upper:]'
COMPUTERS ARE FAST
$ echo "COMPUTERS ARE FAST" | tr '[:upper:]' '[:lower:]'
computers are fast

tr -c SET1 SET2convertit tout caractère non inclus dans SET1 en caractères dans SET2. Dans l'exemple suivant, tous les caractères à l'exception des voyelles indiquées sont remplacés par des espaces.

$ echo "computers are fast" | tr -c '[aeiouy]' ' '
 o  u e   a e  a

tr -dSupprime les caractères spécifiés, mais ne les remplace pas. C'est l'équivalent tr <char> ''.

$ echo "Computers Are Fast" | tr -d '[:lower:]'
C A F

trpeut également remplacer des plages de caractères, par exemple, toutes les lettres entre a et e ou tous les nombres entre 1 et 8, en utilisant la notation s-e, où s est le caractère de départ et e la fin.

$ echo "computers are fast" | tr 'a-e' 'x'
xomputxrs xrx fxst
$ echo "5uch l337 5p34k" | tr '1-4' 'x'
5uch lxx7 5pxxk

La commande tr -s string1compresse toutes les occurrences multiples de caractères string1en un seul. L'une des utilisations les plus utiles tr -sconsiste à remplacer plusieurs espaces consécutifs par un seul.

$ echo "Computers         are       fast" | tr -s ' '
Computers are fast

plier


La commande foldréduit toutes les lignes d'entrée à la largeur spécifiée. Par exemple, il peut être utile de s'assurer que le texte tient sur de petits écrans. Donc, fold -w nempile des chaînes de largeur n caractères.

$ cat ~/Documents/readme | fold -w 16
Thanks again for
 reading this bo
ok!
I hope you're fo
llowing so far!

La commande fold -sne coupera les lignes que sur les caractères d'espace. Il peut être combiné avec le précédent pour limiter la chaîne au nombre spécifié de caractères.

Thanks again
for reading
this book!
I hope you're
following so
far!

sed


sedEst un éditeur de flux non interactif utilisé pour convertir le texte ligne par ligne dans le flux d'entrée. En entrée, soit un fichier ou, ou stdin, en sortie, soit un fichier ou stdout.

Les commandes de l'éditeur peuvent inclure une ou plusieurs adresses , une fonction et des paramètres . Ainsi, les commandes sont les suivantes:

[address[,address]]function[arguments]

Bien qu'il sedremplisse de nombreuses fonctions, nous n'envisagerons de remplacer le texte que comme l'un des cas d'utilisation les plus courants.

Remplacement de texte


La commande de remplacement est sedla suivante:

s/PATTERN/REPLACEMENT/[options]

Exemple : remplacement de la première instance d'un mot dans chaque ligne d'un fichier:

$ cat hello
hello hello
hello world!
hi
$ cat hello | sed 's/hello/Hey I just met you/'
Hey I just met you hello
Hey I just met you world
hi

Nous voyons que dans la première ligne, seule la première instance est remplacée hello. Pour remplacer toutes les occurrences hellodans toutes les lignes, vous pouvez utiliser l'option g(signifie globale ).

$ cat hello | sed 's/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

sedvous permet d'utiliser tous les délimiteurs sauf /, ce qui améliore particulièrement la lisibilité s'il y a des barres obliques dans les arguments de commande eux-mêmes.

$ cat hello | sed 's@hello@Hey I just met you@g'
Hey I just met you Hey I just met you
Hey I just met you world
hi

L'adresse indique à l'éditeur sur quelle ligne ou plage de lignes effectuer la substitution.

$ cat hello | sed '1s/hello/Hey I just met you/g'
Hey I just met you hello
hello world
hi
$ cat hello | sed '2s/hello/Hey I just met you/g'
hello hello
Hey I just met you  world
hi

L'adresse 1indique remplacer hellosur Hey I just met youla première ligne. Nous pouvons spécifier la plage d'adresses dans la notation <start>,<end>, où il <end>peut s'agir soit du numéro de ligne $, soit de la dernière ligne du fichier.

$ cat hello | sed '1,2s/hello/Hey I just met you/g'
Hey I just met you Hey I just met you
Hey I just met you world
hi
$ cat hello | sed '2,3s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi
$ cat hello | sed '2,$s/hello/Hey I just met you/g'
hello hello
Hey I just met you world
hi

Par défaut, il sedproduit le résultat en tant que tel stdout, mais peut également modifier le fichier d'origine avec l'option -i.

$ sed -i '' 's/hello/Bonjour/' sed-data
$ cat sed-data
Bonjour hello
Bonjour world
hi

Remarque. Sous Linux, juste assez -i. Mais sur macOS, le comportement de la commande est légèrement différent, -ivous devez donc l'ajouter juste après ''.

De vrais exemples


Filtrage CSV avec grep et awk


$ grep -w gauge metadata.csv | awk -F, '{ if ($4 == "query") { print $1, "per", $5 } }'
mysql.performance.com_delete per second
mysql.performance.com_delete_multi per second
mysql.performance.com_insert per second
mysql.performance.com_insert_select per second
mysql.performance.com_replace_select per second
mysql.performance.com_select per second
mysql.performance.com_update per second
mysql.performance.com_update_multi per second
mysql.performance.questions per second
mysql.performance.slow_queries per second
mysql.performance.queries per second

Dans cet exemple grep, le fichier metadata.csvfiltre d'abord les lignes contenant le mot gauge, puis celles avec la queryquatrième colonne, et affiche le nom de la métrique (1ère colonne) avec la valeur correspondante per_unit_name(5ème colonne).

Affiche l'adresse IPv4 associée à l'interface réseau


$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

La commande ifconfig <interface name>affiche des informations sur l'interface réseau spécifiée. Par exemple:

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether 19:64:92:de:20:ba
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect
    status: active

Ensuite, courez greppour inet, cela produira deux lignes de correspondance.

$ ifconfig en0 | grep inet
    inet6 fe80::8a3:a1cb:56ae:7c7c%en0 prefixlen 64 secured scopeid 0x7
    inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Ensuite, utilisez grep -vexclure la ligne avec ipv6.

$ ifconfig en0 | grep inet | grep -v inet6
inet 192.168.0.38 netmask 0xffffff00 broadcast 192.168.0.255

Enfin, avec l'aide, nous awkdemandons la deuxième colonne de cette ligne: c'est l'adresse IPv4 associée à notre interface réseau en0.

$ ifconfig en0 | grep inet | grep -v inet6 | awk '{ print $2 }'
192.168.0.38

Remarque. On m'a proposé de remplacer par grep inet | grep -v inet6une équipe aussi fiable awk:

$ ifconfig en0 | awk ' $1 == "inet" { print $2 }'
192.168.0.38

Il est plus court et spécifiquement ciblé sur IPv4 avec la condition $1 == "inet".

Récupération d'une valeur à partir d'un fichier de configuration


$ grep 'editor =' ~/.gitconfig  | cut -d = -f2 | sed 's/ //g'
/usr/bin/vim

Dans le fichier de configuration git de l'utilisateur actuel, recherchez la valeur editor =, recadrez le caractère =, extrayez la deuxième colonne et supprimez tous les espaces autour.

$ grep 'editor =' ~/.gitconfig
     editor = /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2
 /usr/bin/vim
$ grep 'editor =' ~/.gitconfig  | cut -d'=' -f2 | sed 's/ //'
/usr/bin/vim

Extraire les adresses IP d'un fichier journal


Le code réel suivant recherche un message dans le journal de la base de données Too many connections from(suivi d'une adresse IP) et affiche les dix principaux intrus.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c | \
  sort -rn | \
  head -n 10 | \
  awk '{ print $2 }'
   10.11.112.108
   10.11.111.70
   10.11.97.57
   10.11.109.72
   10.11.116.156
   10.11.100.221
   10.11.96.242
   10.11.81.68
   10.11.99.112
   10.11.107.120

Voyons ce que fait ce pipeline. Tout d'abord, à quoi ressemble la ligne dans le journal.

$ grep "Too many connections from" db.log | head -n 1
2020-01-01 08:02:37,617 [myid:1] - WARN  [NIOServerCxn.Factory:1.2.3.4/1.2.3.4:2181:NIOServerCnxnFactory@193] - Too many connections from /10.11.112.108 - max is 60

Il awk '{ print $12 }'extrait ensuite l'adresse IP de la chaîne.

$ grep "Too many connections from" db.log | awk '{ print $12 }'
/10.11.112.108
...

La commande sed 's@/@@'supprime la barre oblique initiale.

$ grep "Too many connections from" db.log | awk '{ print $12 }' | sed 's@/@@'
10.11.112.108
...

Remarque. Comme nous l'avons vu précédemment, sedvous pouvez utiliser n'importe quel séparateur dans. Bien qu'il soit généralement utilisé comme séparateur /, nous remplaçons ici ce caractère particulier, ce qui nuira légèrement à la lisibilité de l'expression de substitution.

sed 's/\///'

sort | uniq -c trie les adresses IP dans l'ordre lexicographique, puis supprime les doublons, en ajoutant le nombre d'entrées avant chaque adresse IP.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c
   1379 10.11.100.221
   1213 10.11.103.168
   1138 10.11.105.177
    946 10.11.106.213
   1211 10.11.106.4
   1326 10.11.107.120
   ...

sort -rn | head -n 10trie les lignes par le nombre d'occurrences, numériquement et dans l'ordre inverse, afin que les principaux contrevenants soient affichés en premier, dont 10 lignes. La dernière commande awk { print $2 }récupère les adresses IP elles-mêmes.

$ grep 'Too many connections from' db.log | \
  awk '{ print $12 }' | \
  sed 's@/@@' | \
  sort | \
  uniq -c | \
  sort -rn | \
  head -n 10 | \
  awk '{ print $2 }'
  10.11.112.108
  10.11.111.70
  10.11.97.57
  10.11.109.72
  10.11.116.156
  10.11.100.221
  10.11.96.242
  10.11.81.68
  10.11.99.112
  10.11.107.120

Renommer une fonction dans le fichier source


Imaginez que nous travaillons sur un projet et que nous aimerions renommer la fonction sous-nommée (ou classe, variable, etc.) dans le fichier source. Vous pouvez le faire avec une commande sed -iqui remplace directement dans le fichier d'origine.

$ cat izk/utils.py
def bool_from_str(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

$ sed -i 's/def bool_from_str/def is_affirmative/' izk/utils.py
$ cat izk/utils.py
def is_affirmative(s):
    if s.isdigit():
        return int(s) == 1
    return s.lower() in ['yes', 'true', 'y']

Remarque. Sur macOS, sed -iutilisez plutôt sed -i ''.

Cependant, nous avons renommé la fonction uniquement dans le fichier d'origine. Cela interrompra l'importation bool_from_strdans tout autre fichier car cette fonction n'est plus définie. Nous devons trouver un moyen de renommer bool_from_strtout au long de notre projet. Ceci peut être réalisé en utilisant des commandes grep, sedainsi que des boucles forou en utilisant xargs.

Approfondissement: cycles foretxargs


Pour remplacer toutes les occurrences de notre projet bool_from_str, vous devez d'abord les trouver récursivement avec grep -r.

$ grep -r bool_from_str .
./tests/test_utils.py:from izk.utils import bool_from_str
./tests/test_utils.py:def test_bool_from_str(s, expected):
./tests/test_utils.py:    assert bool_from_str(s) == expected
./izk/utils.py:def bool_from_str(s):
./izk/prompt.py:from .utils import bool_from_str
./izk/prompt.py:                    default = bool_from_str(os.environ[envvar])

Comme nous ne sommes intéressés que par les fichiers avec des correspondances, vous devez également utiliser l'option -l/--files-with-matches:

-l, --files-with-matches
        Only the names of files containing selected lines are written to standard out-
        put.  grep will only search a file until a match has been found, making
        searches potentially less expensive.  Pathnames are listed once per file
        searched.  If the standard input is searched, the string ``(standard input)''
        is written.

$ grep -r --files-with-matches bool_from_str .
./tests/test_utils.py
./izk/utils.py
./izk/prompt.py

Ensuite, nous pouvons utiliser la commande xargspour effectuer des actions à partir de chaque ligne de la sortie (c'est-à-dire tous les fichiers contenant la ligne bool_from_str).

$ grep -r --files-with-matches bool_from_str . | \
  xargs -n 1 sed -i 's/bool_from_str/is_affirmative/'

L'option -n 1indique que chaque ligne de la sortie doit exécuter une commande distincte sed.

Ensuite, les commandes suivantes sont exécutées:

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

Si la commande avec laquelle vous appelez xargs(dans notre cas sed) prend en charge plusieurs arguments, vous devez ignorer l'argument -n 1relatif aux performances.

grep -r --files-with-matches bool_from_str . | xargs sed -i 's/bool_from_str/is_affirmative/'

Cette commande s'exécutera ensuite

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py ./izk/utils.py ./izk/prompt.py

Remarque. Le synopsis sedde la page de manuel montre que l'équipe peut prendre plusieurs arguments.

SYNOPSIS
     sed [-Ealn] command [file ...]
     sed [-Ealn] [-e command] [-f command_file] [-i extension] [file ...]

En effet, comme nous l'avons vu dans le chapitre précédent, cela file ...signifie que plusieurs arguments sont acceptés, qui sont des noms de fichiers.

Nous constatons que des remplacements sont effectués pour toutes les occurrences bool_from_str.

$ grep -r is_affirmative .
./tests/test_utils.py:from izk.utils import is_affirmative
./tests/test_utils.py:def test_is_affirmative(s, expected):
./tests/test_utils.py:    assert is_affirmative(s) == expected
./izk/utils.py:def is_affirmative(s):
./izk/prompt.py:from .utils import is_affirmative
./izk/prompt.py:                    default = is_affirmative(os.environ[envvar])

Comme cela arrive souvent, il existe plusieurs façons d'obtenir le même résultat. Au lieu de cela, xargsnous pourrions utiliser des boucles forpour parcourir les lignes de la liste et prendre des mesures sur chaque élément. Ces boucles ont la syntaxe suivante:

for item in list; do
    command $item
done

Si nous terminerons notre commande grepdans $(), le shell exécutera dans un sous - shell , dont le résultat sera ensuite répété dans une boucle for.

$ for file in $(grep -r --files-with-matches bool_from_str .); do
  sed -i 's/bool_from_str/is_affirmative/' $file
done

Cette commande s'exécutera

$ sed -i 's/bool_from_str/is_affirmative/' ./tests/test_utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/utils.py
$ sed -i 's/bool_from_str/is_affirmative/' ./izk/prompt.py

La syntaxe de boucle forme semble plus claire que cela xargs, mais cette dernière peut exécuter des commandes en parallèle en utilisant des paramètres -P n, où nest le nombre maximum de commandes parallèles exécutées en même temps, ce qui peut donner un gain de performances.

Sommaire


Tous ces outils ouvrent tout un monde de possibilités, car ils vous permettent d'extraire et de transformer des données, créant des pipelines entiers à partir d'équipes qui n'auraient peut-être jamais été conçues pour travailler ensemble. Chacun d'eux remplit une fonction relativement petite (tri sort, combinaison cat, filtres grep, édition sed, découpe cut, etc.).

Toute tâche comprenant du texte peut être réduite à un pipeline de tâches plus petites, chacune exécutant une action simple et transférant sa sortie à la tâche suivante.

Par exemple, si nous voulons savoir combien d'adresses IP uniques se trouvent dans le fichier journal et que ces adresses IP apparaissent toujours dans la même colonne, nous pouvons exécuter la séquence de commandes suivante:

  • grep chaînes qui correspondent au modèle de chaînes avec des adresses IP
  • recherchez la colonne avec l'adresse IP, extrayez-la avec awk
  • trier la liste des adresses IP à l'aide sort
  • éliminer les doublons adjacents avec uniq
  • compter le nombre de lignes (c'est-à-dire les adresses IP uniques) en utilisant wc -l

Comme il existe de nombreux outils de traitement de texte natifs et tiers, il existe également de nombreuses façons de résoudre tout problème.

Les exemples de cet article étaient farfelus, mais je vous suggère de lire l'article étonnant «Les outils de ligne de commande peuvent être 235 fois plus rapides que votre cluster Hadoop» pour avoir une idée de l'utilité et de la puissance réelle de ces commandes et des problèmes réels ils peuvent décider.

Et après


  1. Comptez le nombre de fichiers et de répertoires situés dans votre répertoire personnel.
  2. .
  3. , .
  4. . .



« » (Essential Tools and Practices for the Aspiring Software Developer) , . , , , , git, SQL, Make, jq , , .

, !

All Articles