Amélioration de l'efficacité du travail, optimisation du système d'exploitation, automatisation, etc. sont les objectifs poursuivis par tout praticien informatique. Dans le système d'exploitation Linux, être capable d'utiliser habilement les outils de redirection et de ligne de commande de pipeline est l'une des compétences qu'il faut maîtriser. Cet article expliquera en détail l'utilisation et les principes des outils de redirection et de pipeline à travers des exemples.
J'aime beaucoup le système Linux, en particulier certaines des conceptions de Linux sont très belles. Par exemple, certains problèmes complexes peuvent être décomposés en plusieurs petits problèmes et peuvent être résolus de manière flexible avec des outils prêts à l'emploi grâce au caractère pipe et à la redirection. Mécanisme.Il est très efficace d'écrire un script shell.
Cet article partagera certains des pièges que j'ai rencontrés lors de l'utilisation des caractères de redirection et de canal dans la pratique. Comprendre certains principes sous-jacents peut améliorer considérablement l'efficacité de l'écriture de scripts.
Les pièges de> et >> Redirection des personnages
Parlons d'abord de la première question, que se passera-t-il si vous exécutez la commande suivante ?
$ cat file.txt > file.txt
Lire et écrire dans le même fichier donne l'impression que rien ne se passera, n'est-ce pas ?
En fait, le résultat de l'exécution de la commande ci-dessus est d'effacer le contenu du fichier file.txt.
PS : Certaines distributions Linux peuvent signaler directement les erreurs. Vous pouvez exécuter catfile.txt pour contourner cette détection.
Comme mentionné dans l'article précédent sur les processus Linux et les descripteurs de fichiers, le programme lui-même n'a pas besoin de se soucier de l'endroit où pointent ses entrées/sorties standard. C'est le shell qui modifie l'emplacement des entrées/sorties standard du programme via des caractères pipe et. symboles de redirection.
Ainsi, lors de l'exécution de la commande cat file.txt > file.txt, le shell ouvrira d'abord file.txt. Puisque le symbole de redirection est >, le contenu du fichier sera effacé, puis le shell définira la sortie standard de. la commande cat dans le fichier .txt, puis la commande cat commence à s'exécuter.
C'est-à-dire le processus suivant :
1. Shell ouvre le fichier.txt et efface son contenu.
2. Shell pointe la sortie standard de la commande cat vers le fichier file.txt.
3. Le shell exécute la commande cat et lit un fichier vide.
4. La commande cat écrit une chaîne vide dans la sortie standard (fichier file.txt).
Donc, le résultat final est que file.txt devient un fichier vide.
Nous savons que > effacera le fichier cible et >> ajoutera du contenu à la fin du fichier cible, alors que se passera-t-il si le symbole de redirection > est remplacé par >> ?
$ echo hello world > file.txt # 文件中只有一行内容 $ cat file.txt >> file.txt # 这个命令会死循环
Une ligne de contenu est d'abord écrite dans file.txt Après avoir exécuté cat file.txt >> file.txt, le résultat attendu devrait être deux lignes de contenu.
Mais malheureusement, le résultat de l'exécution n'est pas celui attendu. Au lieu de cela, il continuera à écrire hello world dans le fichier.txt dans une boucle infinie. Le fichier deviendra bientôt très volumineux et la commande ne pourra être arrêtée qu'avec Control+C.
C'est intéressant, pourquoi y a-t-il une boucle infinie ? En fait, après une petite analyse, vous pouvez penser à la raison :
Tout d'abord, rappelez-vous le comportement de la commande cat. Si vous exécutez uniquement la commande cat, la saisie au clavier sera lue à partir de la ligne de commande. Chaque fois que vous appuyez sur Entrée, la commande cat fera écho à la saisie. La commande cat est une par une. La ligne lit les données puis les génère.
Ensuite, le processus d'exécution de la commande cat file.txt >> file.txt est le suivant :
1. Ouvrez le fichier.txt et préparez-vous à ajouter du contenu à la fin du fichier.
2. Pointez la sortie standard de la commande cat vers le fichier file.txt.
3. La commande cat lit une ligne de contenu dans file.txt et l'écrit dans la sortie standard (ajoutée au fichier file.txt).
4. Puisqu'une ligne de données vient d'être écrite, la commande cat constate qu'il y a encore du contenu pouvant être lu dans file.txt et répétera l'étape 3.
Le processus ci-dessus revient à parcourir la liste et à y ajouter des éléments en même temps. Il ne sera jamais parcouru complètement, ce qui conduit à une boucle infinie de nos commandes.
> Le caractère de redirection et le caractère | pipe fonctionnent ensemble
Nous sommes souvent confrontés au besoin d'intercepter les XX premières lignes du fichier et de supprimer le reste.
Sous Linux, la commande head peut compléter la fonction d'interception des premières lignes d'un fichier :
$ cat file.txt # file.txt 中有五行内容 1 2 3 4 5 $ head -n 2 file.txt # head 命令读取前两行 1 2 $ cat file.txt | head -n 2 # head 也可以读取标准输入 1 2
Si nous voulons conserver les 2 premières lignes du fichier et supprimer le reste, nous pouvons utiliser la commande suivante :
$ head -n 2 file.txt > file.txt
Mais cela entraînera l'erreur mentionnée ci-dessus. En fin de compte, le fichier.txt sera effacé, ce qui ne pourra pas répondre à nos besoins.
Alors si on écrit la commande comme ceci, peut-on éviter les pièges :
$ cat file.txt | head -n 2 > file.txt
La conclusion est que cela ne fonctionne pas, le contenu du fichier sera quand même effacé.
Quoi ? Y a-t-il une fuite dans le pipeline et toutes les données sont perdues ?
Dans l'article précédent sur les processus Linux et les descripteurs de fichiers, j'ai également mentionné le principe d'implémentation du caractère pipe. Essentiellement, il connecte l'entrée et la sortie standard de deux commandes, de sorte que la sortie standard de la commande précédente sert d'entrée standard. de la commande suivante.
Cependant, si vous pensez qu'écrire des commandes comme celle-ci peut obtenir les résultats escomptés, c'est peut-être parce que vous pensez que les commandes connectées par le caractère pipe sont exécutées en série. En fait, plusieurs commandes connectées par le caractère pipe. Les personnages sont exécutés en parallèle.
Vous pensez peut-être que le shell exécutera d'abord la commande cat file.txt, lira normalement tout le contenu de file.txt, puis transmettra ce contenu à la commande head -n 2 > file.txt via un tube.
Bien que le contenu du fichier.txt soit effacé à ce moment-là, head ne lit pas les données du fichier, mais lit les données du tube, il devrait donc être possible d'écrire correctement deux lignes de données dans le fichier.txt.
但实际上,上述理解是错误的,shell 会并行执行管道符连接的命令,比如说执行如下命令:
$ sleep 5 | sleep 5
shell 会同时启动两个sleep进程,所以执行结果是睡眠 5 秒,而不是 10 秒。
这是有点违背直觉的,比如这种常见的命令:
$ cat filename | grep 'pattern'
直觉好像是先执行cat命令一次性读取了filename中所有的内容,然后传递给grep命令进行搜索。
但实际上是cat和grep命令是同时执行的,之所以能得到预期的结果,是因为grep ‘pattern’会阻塞等待标准输入,而cat通过 Linux 管道向grep的标准输入写入数据。
执行下面这个命令能直观感受到cat和grep是在同时执行的,grep在实时处理我们用键盘输入的数据:
$ cat | grep 'pattern'
说了这么多,再回顾一开始的问题:
$ cat file.txt | head -n 2 > file.txt
cat命令和head会并行执行,谁先谁后不确定,执行结果也就不确定。
如果head命令先于cat执行,那么file.txt就会被先清空,cat也就读取不到任何内容;反之,如果cat先把文件的内容读取出来,那么可以得到预期的结果。
不过,通过我的实验(将这种并发情况重复 1w 次)发现,file.txt被清空这种错误情况出现的概率远大于预期结果出现的概率,这个暂时还不清楚是为什么,应该和 Linux 内核实现进程和管道的逻辑有关。
解决方案
说了这么多管道符和重定向符的特点,如何才能避免这个文件被清空的坑呢?
最靠谱的办法就是不要同时对同一个文件进行读写,而是通过临时文件的方式做一个中转。
比如说只保留file.txt文件中的头两行,可以这样写代码:
# 先把数据写入临时文件,然后覆盖原始文件
$ cat file.txt | head -n 2 > temp.txt && mv temp.txt file.txt
这是最简单,最可靠,万无一失的方法。
你如果嫌这段命令太长,也可以通过apt/brew/yum等包管理工具安装moreutils包,就会多出一个sponge命令,像这样使用:
# 先把数据传给 sponge,然后由 sponge 写入原始文件 $ cat file.txt | head -n 2 | sponge file.txt
sponge这个单词的意思是海绵,挺形象的,它会先把输入的数据「吸收」起来,最后再写入file.txt,核心思路和我们使用临时文件时类似的,这个「海绵」就好比一个临时文件,就可以避免同时打开同一个文件进行读写的问题。
在Linux操作系统中,重定向和管道是非常有用的命令行工具,可以让我们更好地掌握系统的运行状态和信息。掌握相关技能能够帮助我们更好地进行系统优化和自动化工作,从而更好地提高工作效率。相信通过本文的介绍,读者对重定向和管道的原理和使用方法都有了更为深入的了解。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!