shell解析命令行的過程以及eval命令

来源:http://www.cnblogs.com/f-ck-need-u/archive/2017/08/25/7426371.html
-Advertisement-
Play Games

本文說明的是一條linux命令在執行時大致要經過哪些過程?以及這些過程的大致順序。 1.1 shell解析命令行 shell讀取和執行命令時的大致操作過程如下圖: 以執行以下命令為例: echo -e "some files:" ~/i* "\nThe date:$(date +%F)\n$name ...


本文說明的是一條linux命令在執行時大致要經過哪些過程?以及這些過程的大致順序。

1.1 shell解析命令行

shell讀取和執行命令時的大致操作過程如下圖:

以執行以下命令為例:

echo -e "some files:" ~/i* "\nThe date:$(date +%F)\n$name's age is $((a+4))" >/tmp/a.log

假設在執行該命令前,已賦值變數"name=longshuai"和"a=24",於是重定向到/tmp/a.log中的結果為:

some files: /root/inotify.sh /root/inotify.sh.ori
The date:2017-08-14
longshuai's age is 28

(1).讀取輸入的命令行。

(2).解析引用並分割命令行為各個單詞,各單詞稱為token。其中重定向所在的token會被保存下來,直到擴展步驟(5)結束後才進行相關處理,如進行擴展、截斷文件等。

shell中有3種引用方式:反斜線引用、單引號引用和雙引號引用。

◇ 反斜線轉義:使得元字元變為普通的字面字元。但這隻能對反斜線後一個字元進行轉義。

◇ 單引號引用:單引號內的所有字元全部變為字面符號符號。但註意:單引號內不能再使用單引號,即使使用了反斜線轉義也不允許。

◇ 雙引號引用:使雙引號內所有字元變為字面符號,但"\"、"$"、"`"(反引號)除外,如果開啟了"!"引用歷史命令時,則感嘆號也除外。

解析引用後,於是就可以將命令行進行單詞分割,分割後的每一部分都稱為一個token。分隔時,不僅分割單個命令,還分割命令列表,所以分隔符包括:空格、tab、分號、管道符號、&、&&、||、重定向符號、圓括弧等。

於是上述命令分割為以下幾個token:

如果分割時發現了管道符號,或者是命令列表等組合了多個命令的情況,則每個命令都的token都相互獨立。

(3).檢查命令行結構。主要檢查是否有命令列表、是否有shell編程結構的命令,如if判斷命令、迴圈結構的for/while/select/until,這些命令屬於保留關鍵字,需要特殊處理。

(4).對第一個token進行別名擴展。如果檢查出它是別名,則擴展後回到(2)再次進行token分解過程。

(5).進行各種擴展。擴展順序為:大括弧擴展;波浪號擴展;參數、變數和命令替換、算術擴展(如果系統支持,此步還進行進程替換);單詞拆分;文件名擴展。

不同引號的引用方式,將改變擴展的起始步驟,正如上圖所畫,沒有任何引號時將從頭到尾全部擴展,使用單引號時將完全不會進行任何擴展,使用雙引號時將從變數替換開始繼續擴展。

①大括弧擴展:如/tmp/{a,b}.log擴展為/tmp/a.log和/tmp/b.log。

②波浪號擴展:擴展為家目錄。如root用戶下的~/.ssh擴展為/root/.ssh。

③變數擴展:即操作和替換變數值。如$a替換為它的值24,${name:-longshuai}替換為longshuai。

④命令替換:此過程將執行命令替換中的命令,並將結果替換到token的對應位置處。

⑤進程替換:將進程的執行結果替換到對應位置。類似於命令替換。替換格式為"<(cmd_list)"和">(cmd_list)",例如"cat <(cat /etc/hosts)"。redhat系列應該都支持進程替換。

⑥算術擴展:計算算術值,並將計算結果替換到對應位置處。例如$((a+4))替換為28。

經過以上幾種擴展後,得到如下結果:

⑦單詞拆分:掃描變數擴展、命令替換和算術擴展的結果,對非引號內的結果按照$IFS的值對這些結果進行單詞分割。

註意,如果沒有進行擴展,或者擴展結果使用引號包圍了,則不會進行此步的單詞拆分。

預設情況下,$IFS值為"   \t\n",所以擴展結果中每遇到空格、製表符、換行符都將被分割為兩個單詞。

這一步其實很容易犯錯,典型的是test命令。例如變數name="Ma longshuai",則test $name == "longshuai"將報錯,因為變數擴展後該語句變為test Ma longshuai == "longshuai",由於是變數替換,所以隨後進行單詞拆分,使得Ma和longshuai被拆分為兩個單詞,但實際上它們共同組成變數name的值。

所以,為了正確操作變數替換和命令替換,儘量將它們使用引號包圍。例如test "$name" == "longshuai",這時將不會進行單詞拆分。

⑧文件名擴展:對每個token進行搜索,將搜索"*"、"?"和"["符號,搜索到了將進行文件名擴展。例如將上面的"/root/i*"擴展為"/root/inotify.sh /root/inotify.sh.ori"。

(6).引號去除。經過上面的過程,該擴展的都擴展了,不需要的引號在此步就可以去掉了。

所以得到如下結果。

(7).搜索和執行命令。

單詞分割後,複雜的命令行將由各個簡單命令結構組成。於是可以搜索每個簡單命令結構的第一個token中的命令,同時還帶有一系列命令選項。例如上面的"echo"和"-e"。

如果命令中不含任何斜杠:

①則先判斷是否有此名稱的shell function存在,如果有則調用它,否則進行下一步搜索。

②判斷該命令是否為bash內置命令,如果是則執行它,否則進行下一步搜索。

③從$PATH的路徑下搜索該命令,如果搜索到了,則執行,否則報錯。

如果命令中包含一個或多個斜杠,則進行相對路徑擴展、絕對路徑查找,找到了則執行,否則報錯。

(8).返回退出狀態碼。

1.2 eval命令

正常情況下,當搜索到命令時將會執行命令,但如果搜索到的命令為eval時,則處理方式有所不同。

它的語法格式為:

eval command arguments

按照前文所述shell解析過程,將最終得到eval command和一系列擴展後的選項、參數,當搜索命令時,搜索到的結果為eval命令,於是eval命令將除了eval命令(以及eval的選項)的所有token再次傳遞給shell進行二次解析。但重定向所在token除外,因為重定向token早已被shell保存下來,所以不會再次截斷文件。

也就是說,"command arguments"被當作eval命令的參數,被傳遞給shell進行解析、執行。

執行過程如下圖所示:

使用示例來說明:

[root@xuexi ~]# a=24;name='long$a'     # 註意,使用的是單引號,禁止$a被擴展

如果直接執行echo $name,則結果為"long$a",但如果執行eval echo $name,結果將是"long24"。

[root@xuexi ~]# eval echo $name
long24

首先shell按照正常過程解析,在變數替換時由於使用了單引號,所以$name第一次變數替換的結果為"long$a",直到命令搜索時發現搜索到的命令是eval命令,執行eval命令,該命令將其參數"echo long$a"再次傳遞給shell,相當於在標準輸入中輸入了"echo long$a",於是shell進行二次解析,這次的變數替換將$a替換為24,最後搜索命令發現是echo命令,於是最終得到"long24"。

關於eval,更多的用法是間接變數$$var的用法,在bash shell中需要在第一個$前加上反引號,即\$$var,這麼做的原因是顯然的:防止第一次shell解析時被當作特殊變數"$$"被擴展。

[root@xuexi ~]# a=b
[root@xuexi ~]# b=haha

[root@xuexi ~]# eval echo \$$a
haha

 

註:本文並非一定准確,只是根據man bash總結而來。如有錯誤,請明確指出。多謝

 

回到系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7426371.html

註:若您覺得這篇文章還不錯請點擊下右下角的推薦,有了您的支持才能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在MySQL中,如何查看一個用戶被授予了那些許可權呢? 授予用戶的許可權可能分全局層級許可權、資料庫層級許可權、表層級別許可權、列層級別許可權、子程式層級許可權。具體分類如下: 全局層級 全局許可權適用於一個給定伺服器中的所有資料庫。這些許可權存儲在mysql.user表中。GRANT ALL ON *.*和REVO... ...
  • 本文屬於《InfluxDB系列教程》文章系列,該系列共包括以下 17 部分: InfluxDB學習之InfluxDB的基本概念 InfluxDB學習之InfluxDB的基本操作 InfluxDB學習之InfluxDB的HTTP API寫入操作 InfluxDB學習之InfluxDB數據保留策略(Re ...
  • 本文屬於《InfluxDB系列教程》文章系列,該系列共包括以下 17 部分: InfluxDB學習之InfluxDB的基本概念 InfluxDB學習之InfluxDB的基本操作 InfluxDB學習之InfluxDB的HTTP API寫入操作 InfluxDB學習之InfluxDB數據保留策略(Re ...
  • 在A表中存在一個欄位“name”,而且不同記錄之間的“name”值有可能會相同,現在就是需要查詢出在該表中的各記錄之間,“name”值存在重覆的項; Select Name,Count(*) From A Group By Name Having Count(*) > 1 查詢work_num中相同 ...
  • 存儲函數 定義 CREATE FUNCTION `fn_sum`(`a` int,`b` int) RETURNS int(11) BEGIN RETURN a + b; END 調用 Navicat工具中的調用 運行結果: Sql調用 SELECT fn_sum(1,2); 運行結果: Java中 ...
  • 要創建新帳戶時分配密碼,請使用 CREATE USER並包含 IDENTIFIED BY子句 要為現有帳戶分配或更改密碼,請使用以下方法之一: 1) 使用ALTER USER帶有IDENTIFIED BY子句的語句: 如果您沒有作為匿名用戶連接,您可以更改自己的密碼,而不必直接命名自己的帳戶: 2) ...
  • GREENPLUM總體結構: 資料庫由Master Severs和Segment Severs通過Interconnect互聯組成。 Master主機負責:建立與客戶端的連接和管理;SQL的解析並形成執行計劃;執行計劃向Segment的分發收集Segment的執行結果;Master不存儲業務數據,只 ...
  • 1. 準備工作 1.1. 軟體準備 1、安裝VMWare 2、在VMWare上安裝CentOS6.5 3、安裝XShell5,用來遠程登錄系統 4、通過rpm -qa | grep ssh 檢查cent os 是否安裝了ssh server和ssh client ,然後使用ssh localhost ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...