自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

尷尬,一不小心把 Linux 管道給整漏了

系統(tǒng) Linux
本文就分享一下我在實踐中使用重定向和管道符遇到的一些坑,搞明白一些底層原理,寫腳本的效率能提升不少。

[[396982]]

我很喜歡 Linux 系統(tǒng),尤其是 Linux 的一些設(shè)計很漂亮,比如可以將一些復(fù)雜的問題分解成若干小問題,通過管道符和重定向機制靈活地用現(xiàn)成的工具解決,寫成 shell 腳本就很高效。

本文就分享一下我在實踐中使用重定向和管道符遇到的一些坑,搞明白一些底層原理,寫腳本的效率能提升不少。

> 和 >> 重定向符的坑

先說第一個問題,執(zhí)行如下命令會發(fā)生什么?

  1. $ cat file.txt > file.txt 

讀取再寫入同一個文件,感覺什么也不會發(fā)生對吧?

實際上,上述命令運行的結(jié)果是清空file.txt文件中的內(nèi)容。

PS:有的 Linux 發(fā)行版可能會直接報錯,可以執(zhí)行cat < file.txt > file.txt繞開這個檢測。

前文 Linux 進程和文件描述符 說過,程序本身沒有必要關(guān)心自己的標(biāo)準(zhǔn)輸入/輸出指向哪里,是 shell 通過管道符和重定向符號修改了程序的標(biāo)準(zhǔn)輸入/輸出的位置。

所以執(zhí)行cat file.txt > file.txt這個命令時,shell 會先打開file.txt,由于重定向符號是>,所以文件中的內(nèi)容會被清空,然后 shell 將cat命令的標(biāo)準(zhǔn)輸出設(shè)置為file.txt,這時候cat命令才開始執(zhí)行。

也就是如下過程:

1、shell 打開file.txt并清空其內(nèi)容。

2、shell 將cat命令的標(biāo)準(zhǔn)輸出指向file.txt文件。

3、shell 執(zhí)行cat命令,讀了一個空文件。

4、cat命令將空字符串寫入標(biāo)準(zhǔn)輸出(file.txt文件)。

所以,最后的結(jié)果就是file.txt變成了空文件。

我們知道,>會清空目標(biāo)文件,>>會在目標(biāo)文件尾部追加內(nèi)容,那么如果將重定向符>改成>>會怎樣呢?

  1. $ echo hello world > file.txt # 文件中只有一行內(nèi)容 
  2. $ cat file.txt >> file.txt # 這個命令會死循環(huán) 

file.txt中首先被寫入一行內(nèi)容,執(zhí)行cat file.txt >> file.txt后預(yù)期的結(jié)果應(yīng)該是兩行內(nèi)容。

但是很遺憾,運行結(jié)果并不符合預(yù)期,而是會死循環(huán)不斷向file.txt中寫入 hello world,文件很快就會變得很大,只能用 Control+C 停止命令。

這就有意思了,為什么會死循環(huán)呢?其實稍加分析就可以想到原因:

首先要回憶cat命令的行為,如果只執(zhí)行cat命令,就會從命令行讀取鍵盤輸入的內(nèi)容,每次按下回車,cat命令就會回顯輸入,也就是說,cat命令是逐行讀取數(shù)據(jù)然后輸出數(shù)據(jù)的。

那么,cat file.txt >> file.txt命令的執(zhí)行過程如下:

1、打開file.txt,準(zhǔn)備在文件尾部追加內(nèi)容。

2、將cat命令的標(biāo)準(zhǔn)輸出指向file.txt文件。

3、cat命令讀取file.txt中的一行內(nèi)容并寫入標(biāo)準(zhǔn)輸出(追加到file.txt文件中)。

4、由于剛寫入了一行數(shù)據(jù),cat命令發(fā)現(xiàn)file.txt中還有可以讀取的內(nèi)容,就會重復(fù)步驟 3。

以上過程,就好比一邊遍歷列表,一遍往列表里追加元素一樣,永遠遍歷不完,所以導(dǎo)致我們的命令死循環(huán)。

> 重定向符和 | 管道符配合

我們經(jīng)常會遇到這樣的需求:截取文件的前 XX 行,其余的都刪除。

在 Linux 中,head命令可以完成截取文件前幾行的功能:

  1. $ cat file.txt # file.txt 中有五行內(nèi)容 
  2. $ head -n 2 file.txt # head 命令讀取前兩行 
  3. $ cat file.txt | head -n 2 # head 也可以讀取標(biāo)準(zhǔn)輸入 

如果我們想保留文件的前 2 行,其他的都刪除,可能會用如下命令:

  1. $ head -n 2 file.txt > file.txt 

但是這就犯了前文說的錯誤,最后file.txt會被清空,不能實現(xiàn)我們的需求。

那我們是這樣寫命令是否可以避坑呢:

  1. $ cat file.txt | head -n 2 > file.txt 

結(jié)論是不行,文件內(nèi)容依然會被清空。

What?是不是管道漏了,把數(shù)據(jù)全漏掉了?

前文 Linux 進程和文件描述符 也說過管道符的實現(xiàn)原理,本質(zhì)上就是將兩個命令的標(biāo)準(zhǔn)輸入和輸出連接起來,讓前一個命令的標(biāo)準(zhǔn)輸出作為下一個命令的標(biāo)準(zhǔn)輸入。

但是,如果你認為這樣寫命令可以得到預(yù)期的結(jié)果,那可能是因為你認為管道符連接的命令是串行執(zhí)行的,這是一個常見的錯誤,實際上管道符連接的多個命令是并行執(zhí)行的。

你可能以為,shell 會先執(zhí)行cat file.txt命令,正常讀取file.txt中的所有內(nèi)容,然后把這些內(nèi)容通過管道傳遞給head -n 2 > file.txt命令。

雖然這時候file.txt中的內(nèi)容會被清空,但是head并沒有從文件中讀取數(shù)據(jù),而是從管道讀取數(shù)據(jù),所以應(yīng)該可以向file.txt正確寫入兩行數(shù)據(jù)。

但實際上,上述理解是錯誤的,shell 會并行執(zhí)行管道符連接的命令,比如說執(zhí)行如下命令:

  1. $ sleep 5 | sleep 5 

shell 會同時啟動兩個sleep進程,所以執(zhí)行結(jié)果是睡眠 5 秒,而不是 10 秒。

這是有點違背直覺的,比如這種常見的命令:

  1. $ cat filename | grep 'pattern' 

直覺好像是先執(zhí)行cat命令一次性讀取了filename中所有的內(nèi)容,然后傳遞給grep命令進行搜索。

但實際上是cat和grep命令是同時執(zhí)行的,之所以能得到預(yù)期的結(jié)果,是因為grep 'pattern'會阻塞等待標(biāo)準(zhǔn)輸入,而cat通過 Linux 管道向grep的標(biāo)準(zhǔn)輸入寫入數(shù)據(jù)。

執(zhí)行下面這個命令能直觀感受到cat和grep是在同時執(zhí)行的,grep在實時處理我們用鍵盤輸入的數(shù)據(jù):

  1. $ cat | grep 'pattern' 

說了這么多,再回顧一開始的問題:

  1. $ cat file.txt | head -n 2 > file.txt 

cat命令和head會并行執(zhí)行,誰先誰后不確定,執(zhí)行結(jié)果也就不確定。

如果head命令先于cat執(zhí)行,那么file.txt就會被先清空,cat也就讀取不到任何內(nèi)容;反之,如果cat先把文件的內(nèi)容讀取出來,那么可以得到預(yù)期的結(jié)果。

不過,通過我的實驗(將這種并發(fā)情況重復(fù) 1w 次)發(fā)現(xiàn),file.txt被清空這種錯誤情況出現(xiàn)的概率遠大于預(yù)期結(jié)果出現(xiàn)的概率,這個暫時還不清楚是為什么,應(yīng)該和 Linux 內(nèi)核實現(xiàn)進程和管道的邏輯有關(guān)。

解決方案

說了這么多管道符和重定向符的特點,如何才能避免這個文件被清空的坑呢?

最靠譜的辦法就是不要同時對同一個文件進行讀寫,而是通過臨時文件的方式做一個中轉(zhuǎn)。

比如說只保留file.txt文件中的頭兩行,可以這樣寫代碼:

  1. # 先把數(shù)據(jù)寫入臨時文件,然后覆蓋原始文件 
  2. $ cat file.txt | head -n 2 > temp.txt && mv temp.txt file.txt 

這是最簡單,最可靠,萬無一失的方法。

你如果嫌這段命令太長,也可以通過apt/brew/yum等包管理工具安裝moreutils包,就會多出一個sponge命令,像這樣使用:

  1. # 先把數(shù)據(jù)傳給 sponge,然后由 sponge 寫入原始文件 
  2. $ cat file.txt | head -n 2 | sponge file.txt 

sponge這個單詞的意思是海綿,挺形象的,它會先把輸入的數(shù)據(jù)「吸收」起來,最后再寫入file.txt,核心思路和我們使用臨時文件時類似的,這個「海綿」就好比一個臨時文件,就可以避免同時打開同一個文件進行讀寫的問題。 

以上就是重定向和管道符的一些坑,希望能幫到你。

 

責(zé)任編輯:武曉燕 來源: labuladong
相關(guān)推薦

2021-10-07 16:45:06

MySQL數(shù)據(jù)庫

2021-01-05 22:49:37

Python編程語言Java

2021-01-30 09:50:54

MySQL密碼服務(wù)器

2023-03-10 08:27:07

for循環(huán)項目線性結(jié)構(gòu)

2021-07-28 05:01:29

Lombok前端測試

2020-10-26 08:56:32

技術(shù)總監(jiān)程序員

2024-05-13 07:58:52

開源項目PR

2018-01-18 22:26:30

2019-09-19 20:47:29

刷臉支付人臉識別人工智能

2020-10-28 15:07:01

Arthas

2022-09-27 18:19:32

Java數(shù)據(jù)結(jié)構(gòu)

2025-03-05 00:27:00

2021-03-29 18:47:53

APP服務(wù)端通信安全數(shù)據(jù)安全

2020-04-30 09:45:41

安卓App小米

2021-03-26 06:14:26

Hashcode項目排查

2022-10-25 17:53:09

Java線程池

2021-01-08 09:36:23

程序員比特幣黑客

2019-07-29 14:38:35

服務(wù)器開發(fā)工具

2020-09-29 07:44:20

跨域前后端分離插件

2021-05-11 16:20:02

網(wǎng)站HTTPHTTPS
點贊
收藏

51CTO技術(shù)棧公眾號