GNU/Linux >> Tutoriels Linux >  >> Linux

csplit :une meilleure façon de diviser un fichier sous Linux en fonction de son contenu

Lorsqu'il s'agit de diviser un fichier texte en plusieurs fichiers sous Linux, la plupart des gens utilisent la commande split. Rien de mal avec la commande split sauf qu'elle s'appuie sur la taille en octets ou la taille de ligne pour diviser les fichiers.

Ce n'est pas pratique dans les situations où vous devez diviser des fichiers en fonction de leur contenu plutôt que de leur taille. Laissez-moi vous donner un exemple.

Je gère mes tweets programmés à l'aide de fichiers YAML. Un fichier de tweet typique contient plusieurs tweets, séparés par quatre tirets :

  ----
    event:
      repeat: { days: 180 }
    status: |
      I think I use the `sed` command daily. And you?

      https://www.yesik.it/EP07
      #Shell #Linux #Sed #YesIKnowIT
  ----
    status: |
      Print the first column of a space-separated data file:
      awk '{print $1}' data.txt # Print out just the first column

      For some unknown reason, I find that easier to remember than:
      cut -f1 data.txt

      #Linux #AWK #Cut
  ----
    status: |
      For the #shell #beginners :
[...]

Lors de leur importation dans mon système, je dois écrire chaque tweet dans son propre fichier. Je fais cela pour éviter d'enregistrer des tweets en double.

Mais comment scinder un fichier en plusieurs parties en fonction de son contenu ? Eh bien, vous pouvez probablement obtenir quelque chose de convaincant en utilisant les commandes awk :

  sh$ awk < tweets.yaml '
  >     /----/ { OUTPUT="tweet." (N++) ".yaml" }
  >     { print > OUTPUT }
  > '

Cependant, malgré une relative simplicité, une telle solution n'est pas très robuste :par exemple, je n'ai pas fermé correctement les différents fichiers de sortie, donc cela pourrait très bien atteindre la limite des fichiers ouverts. Ou que se passe-t-il si j'ai oublié le séparateur avant le tout premier tweet du fichier ? Bien sûr, tout cela peut être géré et corrigé dans le script AWK, au prix de le rendre plus complexe. Mais pourquoi s'embêter avec ça quand on a le csplit outil pour accomplir cette tâche ?

Utiliser csplit pour diviser des fichiers sous Linux

Le csplit l'outil est un cousin du split outil qui peut être utilisé pour diviser un fichier en taille fixe morceaux. Mais csplit identifiera les limites des blocs en fonction du contenu du fichier, plutôt qu'en utilisant le nombre d'octets.

Dans ce didacticiel, je vais démontrer l'utilisation de la commande csplit et expliquer également la sortie de cette commande.

Ainsi, par exemple, si je souhaite diviser mon fichier tweet en fonction du ---- délimiteur, je pourrais écrire :

  sh$ csplit tweets.yaml /----/
  0
  10846

Vous avez peut-être deviné le csplit L'outil a utilisé l'expression régulière fournie sur la ligne de commande pour identifier le séparateur. Et quels pourraient être ces 0 et 10983 résultat affiché sur la sortie standard ? Eh bien, ils sont la taille en octets de chaque bloc de données créé.

  sh$ ls -l xx0*
  -rw-r--r-- 1 sylvain sylvain     0 Jun  6 11:30 xx00
  -rw-r--r-- 1 sylvain sylvain 10846 Jun  6 11:30 xx01

Attendez une minute! Où ces xx00 et xx01 d'où proviennent les noms de fichiers ? Et pourquoi csplit diviser le fichier en deux morceaux seulement ? Et pourquoi le premier bloc de données a une longueur de zéro octet ?

La réponse à la première question est simple :xxNN (ou plus formellement xx%02d ) est le format de nom de fichier par défaut utilisé par csplit . Mais vous pouvez changer cela en utilisant le --suffix-format et --prefix options. Par exemple, je pourrais changer le format en quelque chose de plus significatif pour mes besoins :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     /----/
  0
  10846

  sh$ ls -l tweet.*
  -rw-r--r-- 1 sylvain sylvain     0 Jun  6 11:30 tweet.000.yaml
  -rw-r--r-- 1 sylvain sylvain 10846 Jun  6 11:30 tweet.001.yaml

Le préfixe est une chaîne simple, mais le suffixe est une chaîne de format comme celle utilisée par la bibliothèque C standard printf une fonction. La plupart des caractères du format seront utilisés textuellement, à l'exception des spécifications de conversion qui sont introduites par le signe pourcentage (% ) et qui se termine par un spécificateur de conversion (ici, d ). Entre les deux, le format peut également contenir divers drapeaux et options. Dans mon exemple, le %03d spécification de conversion signifie :

  • afficher le numéro de bloc sous forme d'entier décimal (d ),
  • dans un champ de trois caractères (3 ),
  • éventuellement complété à gauche par des zéros (0 ).

Mais cela ne répond pas aux autres interrogations que j'avais ci-dessus :pourquoi n'en avons-nous que deux morceaux, l'un d'eux contenant zéro octets ? Peut-être avez-vous déjà trouvé la réponse à cette dernière question par vous-même :mon fichier de données commence par ---- sur sa toute première ligne. Donc, csplit l'a considéré comme un délimiteur, et comme il n'y avait pas de données avant cette ligne, il a créé un premier bloc vide. Nous pouvons désactiver la création de fichiers de longueur zéro octet en utilisant le --elide-empty-files choix :

  sh$ rm tweet.*
  rm: cannot remove 'tweet.*': No such file or directory
  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/
  10846

  sh$ ls -l tweet.*
  -rw-r--r-- 1 sylvain sylvain 10846 Jun  6 11:30 tweet.000.yaml

Ok :plus de fichiers vides. Mais dans un sens, le résultat est pire maintenant, puisque csplit diviser le fichier en un seul tronçon. Nous pouvons à peine appeler cela "diviser" un fichier, n'est-ce pas ?

L'explication de ce résultat surprenant est csplit ne pas supposez que chaque mandrin doit être divisé en fonction du même séparateur. En fait, csplit vous oblige à fournir chaque séparateur utilisé. Même si c'est plusieurs fois la même :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/ /----/ /----/
  170
  250
  10426

J'ai mis trois séparateurs (identiques) sur la ligne de commande. Donc, csplit identifié la fin du premier morceau en fonction du premier séparateur. Cela conduit à un bloc de zéro octet qui a été élidé. Le deuxième morceau était délimité par la ligne suivante correspondant à /----/ . Conduisant à un morceau de 170 octets. Enfin, un troisième bloc de 250 octets a été identifié sur la base du troisième séparateur. Les données restantes, 10 426 octets, ont été placées dans le dernier bloc.

  sh$ ls -l tweet.???.yaml
  -rw-r--r-- 1 sylvain sylvain   170 Jun  6 11:30 tweet.000.yaml
  -rw-r--r-- 1 sylvain sylvain   250 Jun  6 11:30 tweet.001.yaml
  -rw-r--r-- 1 sylvain sylvain 10426 Jun  6 11:30 tweet.002.yaml

Évidemment, ce ne serait pas pratique si nous devions fournir autant de séparateurs sur la ligne de commande qu'il y a de morceaux dans le fichier de données. D'autant plus que ce nombre exact n'est généralement pas connu à l'avance. Heureusement, csplit a un motif spécial signifiant "répéter le motif précédent autant que possible." Malgré sa syntaxe rappelant le quantificateur étoile dans une expression régulière, celui-ci est plus proche du concept de Kleene plus puisqu'il est utilisé pour répéter un séparateur qui a déjà été mis en correspondance une fois :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/ '{*}'
  170
  250
  190
  208
  140
[...]
  247
  285
  194
  214
  185
  131
  316
  221

Et cette fois, enfin, j'ai divisé ma collection de tweets en parties individuelles. Cependant, est-ce que csplip avez-vous d'autres jolis motifs "spéciaux" comme ça ? Eh bien, je ne sais pas si nous pouvons les appeler "spéciaux", mais définitivement, csplit mieux comprendre les modèles.

Plus de modèles csplit

Nous venons de voir dans la section précédente comment utiliser le quantificateur ‘{*}’ pour les répétitions non liées. Cependant, en remplaçant l'étoile par un chiffre, vous pouvez demander un nombre exact de répétitions :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/ '{6}'
  170
  250
  190
  208
  140
  216
  9672

Cela conduit à un cas d'angle intéressant. Qu'est-ce qui s'ajouterait si le nombre de répétitions dépassait le nombre de délimiteurs réels dans le fichier de données ? Eh bien, voyons cela sur un exemple :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/ '{999}'
  csplit: ‘/----/’: match not found on repetition 62
  170
  250
  190
  208
[...]
  91
  247
  285
  194
  214
  185
  131
  316
  221

  sh$ ls tweet.*
  ls: cannot access 'tweet.*': No such file or directory

Fait intéressant, non seulement csplit signalé une erreur, mais il a également supprimé tous les fichiers de blocs créés au cours du processus. Portez une attention particulière à ma formulation :elle est supprimée eux. Cela signifie que les fichiers ont été créés, alors, lorsque csplit rencontré l'erreur, il les a supprimés. En d'autres termes, si vous avez déjà un fichier dont le nom ressemble à un fichier fragmenté, il sera supprimé :

  sh$ touch tweet.002.yaml
  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     /----/ '{999}'
  csplit: ‘/----/’: match not found on repetition 62
  170
  250
  190
[...]
  87
  91
  247
  285
  194
  214
  185
  131
  316
  221

  sh$ ls tweet.*
  ls: cannot access 'tweet.*': No such file or directory

Dans l'exemple ci-dessus, le tweet.002.yaml le fichier que nous avons créé manuellement a été écrasé, puis supprimé par csplit .

Vous pouvez modifier ce comportement en utilisant le --keep-files option. Comme son nom l'indique, il ne supprimera pas les morceaux créés par csplit après avoir rencontré une erreur :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     --keep-files \
  >     /----/ '{999}'
  csplit: ‘/----/’: match not found on repetition 62
  170
  250
  190
[...]
  316
  221

  sh$ ls tweet.*
  tweet.000.yaml
  tweet.001.yaml
  tweet.002.yaml
  tweet.003.yaml
[...]
  tweet.058.yaml
  tweet.059.yaml
  tweet.060.yaml
  tweet.061.yaml

Remarquez dans ce cas, et malgré l'erreur, csplit n'a supprimé aucune donnée :

  sh$ diff -s tweets.yaml <(cat tweet.*)
  Files tweets.yaml and /dev/fd/63 are identical

Mais que se passe-t-il s'il y a des données dans le fichier que je veux supprimer ? Eh bien, csplit a un support limité pour cela en utilisant un %regex% motif.

Saut de données dans csplit

Lors de l'utilisation d'un signe de pourcentage (% ) comme délimiteur de regex au lieu d'une barre oblique (/ ), csplit va ignorer données jusqu'à (sans inclure) la première ligne correspondant à l'expression régulière. Cela peut être utile pour ignorer certains enregistrements, notamment au début ou à la fin du fichier d'entrée :

  sh$ # Keep only the first two tweets
  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     --keep-files \
  >     /----/ '{2}' %----% '{*}'
  170
  250

  sh$ head tweet.00[012].yaml
  ==> tweet.000.yaml <==
  ----
    event:
      repeat: { days: 180 }
    status: |
      I think I use the `sed` command daily. And you?

      https://www.yesik.it/EP07
      #Shell #Linux #Sed #YesIKnowIT

  ==> tweet.001.yaml <==
  ----
    status: |
      Print the first column of a space-separated data file:
      awk '{print $1}' data.txt # Print out just the first column

      For some unknown reason, I find that easier to remember than:
      cut -f1 data.txt

      #Linux #AWK #Cut
  sh$ # Skip the first two tweets
  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     --keep-files \
  >     %----% '{2}' /----/ '{2}'
  190
  208
  140
  9888

  sh$ head tweet.00[012].yaml
  ==> tweet.000.yaml <==
  ----
    status: |
      For the #shell #beginners :
      « #GlobPatterns : how to move hundreds of files in not time [1/3] »
      https://youtu.be/TvW8DiEmTcQ

      #Unix #Linux
      #YesIKnowIT

  ==> tweet.001.yaml <==
  ----
    status: |
      Want to know the oldest file in your disk?

      find / -type f -printf '%TFT%.8TT %p\n' | sort | less
      (should work on any Single UNIX Specification compliant system)
      #UNIX #Linux

  ==> tweet.002.yaml <==
  ----
    status: |
      When using the find command, use `-iname` instead of `-name` for case-insensitive search
      #Unix #Linux #Shell #Find
  sh$ # Keep only the third and fourth tweets
  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     --keep-files \
  >     %----% '{2}' /----/ '{2}' %----% '{*}'
  190
  208
  140

  sh$ head tweet.00[012].yaml
  ==> tweet.000.yaml <==
  ----
    status: |
      For the #shell #beginners :
      « #GlobPatterns : how to move hundreds of files in not time [1/3] »
      https://youtu.be/TvW8DiEmTcQ

      #Unix #Linux
      #YesIKnowIT

  ==> tweet.001.yaml <==
  ----
    status: |
      Want to know the oldest file in your disk?

      find / -type f -printf '%TFT%.8TT %p\n' | sort | less
      (should work on any Single UNIX Specification compliant system)
      #UNIX #Linux

  ==> tweet.002.yaml <==
  ----
    status: |
      When using the find command, use `-iname` instead of `-name` for case-insensitive search
      #Unix #Linux #Shell #Find

Utilisation des décalages lors du fractionnement de fichiers avec csplit

Lors de l'utilisation d'expressions régulières (soit /…​/ ou %…​% ) vous pouvez spécifier un positif (+N ) ou négatif (-N ) décalage à la fin du motif donc csplit divisera le fichier N lignes après ou avant la ligne correspondante. N'oubliez pas que dans tous les cas, le modèle spécifie la fin du morceau :

  sh$ csplit tweets.yaml \
  >     --prefix='tweet.' --suffix-format='%03d.yaml' \
  >     --elide-empty-files \
  >     --keep-files \
  >     %----%+1 '{2}' /----/+1 '{2}' %----% '{*}'
  190
  208
  140

  sh$ head tweet.00[012].yaml
  ==> tweet.000.yaml <==
    status: |
      For the #shell #beginners :
      « #GlobPatterns : how to move hundreds of files in not time [1/3] »
      https://youtu.be/TvW8DiEmTcQ

      #Unix #Linux
      #YesIKnowIT
  ----

  ==> tweet.001.yaml <==
    status: |
      Want to know the oldest file in your disk?

      find / -type f -printf '%TFT%.8TT %p\n' | sort | less
      (should work on any Single UNIX Specification compliant system)
      #UNIX #Linux
  ----

  ==> tweet.002.yaml <==
    status: |
      When using the find command, use `-iname` instead of `-name` for case-insensitive search
      #Unix #Linux #Shell #Find
  ----

Fractionner par numéro de ligne

Nous avons déjà vu comment nous pouvons utiliser une expression régulière pour diviser des fichiers. Dans ce cas, csplit divisera le fichier à la première ligne correspondant cette expression régulière. Mais vous pouvez aussi identifier la ligne fractionnée par son numéro de ligne comme nous allons le voir maintenant.

Avant de passer à YAML, je stockais mes tweets programmés dans un fichier plat.

Dans ce fichier, un tweet était composé de deux lignes. L'un contenant une répétition facultative, et le second contenant le texte du tweet, avec des retours à la ligne remplacés par \n. Encore une fois, cet exemple de fichier est disponible en ligne.

Avec ce format de "taille fixe", il était également possible d'utiliser csplit pour mettre chaque tweet individuel dans son propre fichier :

  sh$ csplit tweets.txt \
  >     --prefix='tweet.' --suffix-format='%03d.txt' \
  >     --elide-empty-files \
  >     --keep-files \
  >     2 '{*}'
  csplit: ‘2’: line number out of range on repetition 62
  1
  123
  222
  161
  182
  119
  184
  81
  148
  128
  142
  101
  107
[...]
  sh$ diff -s tweets.txt <(cat tweet.*.txt)
  Files tweets.txt and /dev/fd/63 are identical
  sh$ head tweet.00[012].txt
  ==> tweet.000.txt <==


  ==> tweet.001.txt <==
  { days:180 }
  I think I use the `sed` command daily. And you?\n\nhttps://www.yesik.it/EP07\n#Shell #Linux #Sed\n#YesIKnowIT

  ==> tweet.002.txt <==
  {}
  Print the first column of a space-separated data file:\nawk '{print $1}' data.txt # Print out just the first column\n\nFor some unknown reason, I find that easier to remember than:\ncut -f1 data.txt\n\n#Linux #AWK #Cut

L'exemple ci-dessus semble facile à comprendre, mais il y a ici deux pièges. Tout d'abord, le 2 donné en argument de csplit est un numéro de ligne , pas une ligne compte . Cependant, lors de l'utilisation d'une répétition comme je l'ai fait, après la première correspondance, csplit utilisera ce nombre comme ligne compter . Si ce n'est pas clair, je vous laisse comparer le résultat des trois commandes suivantes :

  sh$ csplit tweets.txt --keep-files 2 2 2 2 2
  csplit: warning: line number ‘2’ is the same as preceding line number
  csplit: warning: line number ‘2’ is the same as preceding line number
  csplit: warning: line number ‘2’ is the same as preceding line number
  csplit: warning: line number ‘2’ is the same as preceding line number
  1
  0
  0
  0
  0
  9030
  sh$ csplit tweets.txt --keep-files 2 4 6 8 10
  1
  123
  222
  161
  182
  8342
  sh$ csplit tweets.txt --keep-files 2 '{4}'
  1
  123
  222
  161
  182
  8342

J'ai mentionné un deuxième écueil, un peu lié au premier. Peut-être avez-vous remarqué la ligne vide tout en haut du tweets.txt dossier? Cela mène à ce tweet.000.txt morceau qui contient uniquement le caractère de nouvelle ligne. Malheureusement, c'était nécessaire dans cet exemple à cause de la répétition :rappelez-vous que je veux deux lignes morceaux. Donc le 2 est obligatoire avant la répétition. Mais cela signifie aussi le premier le morceau se cassera à, mais n'incluant pas , la ligne deux. En d'autres termes, le premier bloc contient une ligne. Tous les autres contiendront 2 lignes. Vous pourriez peut-être partager votre opinion dans la section des commentaires, mais en ce qui me concerne, je pense que c'était un choix de conception malheureux.

Vous pouvez atténuer ce problème en passant directement à la première ligne non vide :

  sh$ csplit tweets.txt \
  >     --prefix='tweet.' --suffix-format='%03d.txt' \
  >     --elide-empty-files \
  >     --keep-files \
  >     %.% 2 '{*}'
  csplit: ‘2’: line number out of range on repetition 62
  123
  222
  161
[...]
  sh$ head tweet.00[012].txt
  ==> tweet.000.txt <==
  { days:180 }
  I think I use the `sed` command daily. And you?\n\nhttps://www.yesik.it/EP07\n#Shell #Linux #Sed\n#YesIKnowIT

  ==> tweet.001.txt <==
  {}
  Print the first column of a space-separated data file:\nawk '{print $1}' data.txt # Print out just the first column\n\nFor some unknown reason, I find that easier to remember than:\ncut -f1 data.txt\n\n#Linux #AWK #Cut

  ==> tweet.002.txt <==
  {}
  For the #shell #beginners :\n« #GlobPatterns : how to move hundreds of files in not time [1/3] »\nhttps://youtu.be/TvW8DiEmTcQ\n\n#Unix #Linux\n#YesIKnowIT

Lecture depuis stdin

Bien sûr, comme la plupart des outils en ligne de commande, csplit peut lire les données d'entrée à partir de son entrée standard. Dans ce cas, vous devez spécifier - comme nom de fichier d'entrée :

  sh$ tr [:lower:] [:upper:] < tweets.txt | csplit - \
  >     --prefix='tweet.' --suffix-format='%03d.txt' \
  >     --elide-empty-files \
  >     --keep-files \
  >     %.% 2 '{3}'
  123
  222
  161
  8524

  sh$ head tweet.???.txt
  ==> tweet.000.txt <==
  { DAYS:180 }
  I THINK I USE THE `SED` COMMAND DAILY. AND YOU?\N\NHTTPS://WWW.YESIK.IT/EP07\N#SHELL #LINUX #SED\N#YESIKNOWIT

  ==> tweet.001.txt <==
  {}
  PRINT THE FIRST COLUMN OF A SPACE-SEPARATED DATA FILE:\NAWK '{PRINT $1}' DATA.TXT # PRINT OUT JUST THE FIRST COLUMN\N\NFOR SOME UNKNOWN REASON, I FIND THAT EASIER TO REMEMBER THAN:\NCUT -F1 DATA.TXT\N\N#LINUX #AWK #CUT

  ==> tweet.002.txt <==
  {}
  FOR THE #SHELL #BEGINNERS :\N« #GLOBPATTERNS : HOW TO MOVE HUNDREDS OF FILES IN NOT TIME [1/3] »\NHTTPS://YOUTU.BE/TVW8DIEMTCQ\N\N#UNIX #LINUX\N#YESIKNOWIT

  ==> tweet.003.txt <==
  {}
  WANT TO KNOW THE OLDEST FILE IN YOUR DISK?\N\NFIND / -TYPE F -PRINTF '%TFT%.8TT %P\N' | SORT | LESS\N(SHOULD WORK ON ANY SINGLE UNIX SPECIFICATION COMPLIANT SYSTEM)\N#UNIX #LINUX
  {}
  WHEN USING THE FIND COMMAND, USE `-INAME` INSTEAD OF `-NAME` FOR CASE-INSENSITIVE SEARCH\N#UNIX #LINUX #SHELL #FIND
  {}
  FROM A POSIX SHELL `$OLDPWD` HOLDS THE NAME OF THE PREVIOUS WORKING DIRECTORY:\NCD /TMP\NECHO YOU ARE HERE: $PWD\NECHO YOU WERE HERE: $OLDPWD\NCD $OLDPWD\N\N#UNIX #LINUX #SHELL #CD
  {}
  FROM A POSIX SHELL, "CD" IS A SHORTHAND FOR CD $HOME\N#UNIX #LINUX #SHELL #CD
  {}
  HOW TO MOVE HUNDREDS OF FILES IN NO TIME?\NUSING THE FIND COMMAND!\N\NHTTPS://YOUTU.BE/ZMEFXJYZAQK\N#UNIX #LINUX #MOVE #FILES #FIND\N#YESIKNOWIT

Et c'est à peu près tout ce que je voulais vous montrer aujourd'hui. J'espère qu'à l'avenir, vous utiliserez csplit pour diviser des fichiers sous Linux. Si vous avez apprécié cet article, n'oubliez pas de le partager et de l'aimer sur votre réseau social préféré !


Linux
  1. Comment diviser un seul fichier en plusieurs fichiers en fonction des lignes

  2. Linux - Tout est un fichier ?

  3. 9 exemples utiles de la commande Split sous Linux

  4. Introduction à Linux KVM (Kernel Based Virtualization) et ses avantages

  5. Comment diviser un iso ou un fichier à l'aide de la commande 'split' sous Linux

Comment éditer un fichier sans changer ses horodatages sous Linux

Comment trouver des fichiers basés sur l'horodatage sous Linux

Pourquoi Linux est-il si mauvais et Windows 11 meilleur à tous points de vue ?

5 commandes pour afficher le contenu d'un fichier en ligne de commande Linux

Findmnt - Meilleur moyen de trouver des systèmes de fichiers montés sur Linux

Tutoriel Tripwire :Système de détection d'intrusion basé sur l'hôte Linux