shell 是否具有與其他指令相同的標準輸入、標準輸出、標準錯誤?

shell 是否具有與其他指令相同的標準輸入、標準輸出、標準錯誤?

例如,bash位於/bin/bash下,這意味著它是一個命令,每個命令都有三個(0,1,2)孔:標準輸入,標準輸出,標準錯誤。

這對 shell 來說也是 100% 正確嗎?

答案1

它與任何其他程序相同。這允許您像其他程式一樣重定向和管道 I/O。

echo "cat filename" | bash

當從管道讀取其標準輸入時將執行該cat filename命令。bash

bash -c "echo foo" > filename

將執行該echo foo命令,並將輸出重定向到該檔案。

在 Unix 上,shell 沒有什麼「特別」的地方。它只是一個普通的程序,其主要目的是執行其他程序。

答案2

讓我們區分一些術語:

A命令是你在 shell 中輸入的內容。它可能是一個別名或一個外殼函數,或者它可能指的是執行檔

一個執行檔可能是一個二進位執行檔(即直接包含機器碼的代碼)或腳本。腳本包括bash腳本、sh腳本、perl腳本、awk腳本、sed腳本、python腳本等。等人。

腳本的前兩個位元組(如果要直接作為可執行檔執行)必須是#!,這是一個“幻數”,向內核發出信號以讀取更多字節,直到讀取換行符,將這些位元組解釋為通往a的路徑二進位執行檔,可能有一個由空格分隔的參數(例如#!/bin/awk -f),然後執行二進位可執行文件,腳本本身的路徑作為參數傳入。

最終,核心唯一可以真正執行的是機器碼,即二進位可執行檔。二進位可執行文件,例如 sh、bash、perl、python、awk 等。等人。叫做口譯員。 他們解釋腳本並執行其指令。但它們必須存在於機器碼本身才能執行。

當程式(二進位可執行檔)實際由核心運行時,它作為過程。 二進位可執行檔只是一個包含指令的檔案。 A流程是「程式的運行實例」;更具體地說,它是內建於核心中的一個抽象,具有關聯的記憶體、環境變數、進程 ID (PID)、檔案描述符它可用於輸入和輸出以及其他屬性。您可以「同時」多次運行一個可執行檔(實際上並不是在單核心機器上同時運行,而是由於核心分配 CPU 週期的方式,它會似乎同時)並且每個正在運行的實例將是不同的進程,即使它們都是同一程式的實例。

0、1 和 2(標準輸入、標準輸出和標準誤差)是文件描述符。 說實話,它們的存在只是按照慣例。您可以使用 C 建立一個程序,該程序將啟動(運行)其他程序(執行各種二進位可執行檔),而無需向它們提供這些文件描述符。 然而,由於標準程序都是在假設文件描述符 0、1 和 2 可用的情況下編寫的,因此您可能只會得到錯誤(在大多數情況下),並且程式將無法正常工作。

要真正完全理解這一點,您應該理解一個過程是如何產生的。 這有點像是誕生的奇蹟。 ;) 所有行程都必須由其他過程。不用擔心啟動系統時第一個進程如何啟動,PID 為 1 的進程稱為“init”,它會啟動作業系統運行所需的其他基本進程。

一個行程如何啟動另一個行程有兩個基本步驟:執行。 這兩個都是系統調用,也就是說它們是進程發送的操作/請求到內核只有內核才能真正實現。

「Fork」的意思是(簡而言之)「內核,請給我複製一份」。 (「Me」是一個正在運行的進程。)核心製作進程的完整副本——它的檔案描述符、記憶體、執行狀態(它在遵循組成它是實例的程式的指令時所處的位置),環境變數等等。所以它是該過程的“克隆”。現在你要如何區分原件和抄本呢?僅透過一件事:系統呼叫的返回狀態fork。子進程取得「0」(成功),父進程取得新建立的子進程的PID。因此,透過檢查此返回狀態,每個進程都可以弄清楚它現在應該做什麼(因為記住,它們遵循同一組指令!)。

“Exec”實際上是“execve()”。簡而言之,它詢問內核:「內核,請代替我(我是一個進程),具有 ______ 文件中指定的程式實例。論點新流程將具有的,以及環境它將擁有的(環境變數數組)。

因此,當您在 shell 中鍵入命令時,真正發生的事情(大多數時候,忽略特殊情況,例如 shell 內建命令,例如cd)是您的 shell(這是正在運行的進程)叉子,進而執行長您指定的命令。

如果您已經完成了輸出或輸入重定向/bin/echo hello > /dev/null,那麼分叉的子進程exec在 ing echo之前將相應地調整其文件描述符,以便文件描述符 1(在本例中)被綁定到/dev/null而不是您的終端或之前的任何位置。

所以是的,可執行檔的任何正在運行的實例/bin/bash都期望有一個可用的文件描述符0(可以從中讀取輸入)、一個文件描述符1(可以寫入輸出)以及一個文件描述符2(可以讀取和寫入錯誤)訊息和類似的輸入/輸出。

相關內容