天天看點

Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

Sevice Computing:CLI 指令行實用程式開發基礎

  • 1、概述
  • 2、基礎知識
    • 指令
    • 指令行參數
    • 選項:長格式、短格式
    • IO:stdin、stdout、stderr、管道、重定向
    • 環境變量
    • Golang之使用Flag和Pflag
  • 3、開發實踐
    • slepg程式邏輯及源代碼了解
    • 代碼實作
    • 測試運作

1、概述

CLI(Command Line Interface)實用程式是Linux下應用開發的基礎。正确的編寫指令行程式讓應用與作業系統融為一體,通過shell或script使得應用獲得最大的靈活性與開發效率。Linux提供了cat、ls、copy等指令與作業系統互動;go語言提供一組實用程式完成從編碼、編譯、庫管理、産品釋出全過程支援;容器服務如docker、k8s提供了大量實用程式支撐雲服務的開發、部署、監控、通路等管理任務;git、npm等都是大家比較熟悉的工具。盡管作業系統與應用系統服務可視化、圖形化,但在開發領域,CLI在程式設計、調試、運維、管理中提供了圖形化程式不可替代的靈活性與效率。

2、基礎知識

指令行是程式與使用者進行互動的一種手段,具有可靠的複雜指令行參數處理機制,會使得應用程式更好、更有用。

通過閱讀老師給的兩篇參考資料,基本了解了POSIX/GNU 指令行接口的一些概念與規範。指令行程式主要涉及内容:

  • 指令
  • 指令行參數
  • 選項:長格式、短格式
  • IO:stdin、stdout、stderr、管道、重定向
  • 環境變量

指令

  • 指令行準則:

通用 Linux 實用程式的編寫者應該在代碼中遵守某些準則。這些準則經過了長期發展,它們有助于確定使用者以更靈活的方式使用實用程式,特别是在與其它指令(内置的或使用者編寫的)以及 shell 的協作方面 ― 這種協作是利用 Linux 作為開發環境的能力的手段之一。selpg 實用程式用執行個體說明了下面列出的所有準則和特性。(注:在接下來的那些示例中,“$”符号代表 shell 提示符,不必輸入它。)

準則1:輸入

應該允許輸入來自以下兩種方式:

在指令行上指定的檔案名。例如:

$ command input_file

在這個例子中,command 應該讀取檔案 input_file。

标準輸入(stdin),預設情況下為終端(也就是使用者的鍵盤)。例如:

$ command

這裡,使用者輸入 Control-D(檔案結束訓示符)前輸入的所有内容都成為 command 的輸入。

準則2:輸出

輸出應該被寫至标準輸出,預設情況下标準輸出同樣也是終端(也就是使用者的螢幕):

$ command

在這個例子中,command 的輸出出現在螢幕上。

準則 3. 錯誤輸出

錯誤輸出應該被寫至标準錯誤(stderr),預設情況下标準錯誤同樣也是終端(也就是使用者的螢幕):

$ command

這裡,運作 command 時出現的任何錯誤消息都将被寫至螢幕。

但是使用标準錯誤重定向,也可以将錯誤重定向至檔案。例如:

$ command 2>error_file

在這個例子中,command 的正常輸出在螢幕顯示,而任何錯誤消息都被寫至 error_file。

可以将标準輸出和标準錯誤都重定向至不同的檔案,如下所示:

$ command >output_file 2>error_file

這裡,将标準輸出寫至 output_file,而将所有寫至标準錯誤的内容都寫至 error_file。

準則 4. 執行

程式應該有可能既獨立運作,也可以作為管道的一部分運作,如上面的示例所示。該特性可以重新叙述如下:不管程式的輸入源(檔案、管道或終端)和輸出目的地是什麼,程式都應該以同樣的方式工作。這使得在如何使用它方面有最大的靈活性。

準則 4. 執行

程式應該有可能既獨立運作,也可以作為管道的一部分運作,如上面的示例所示。該特性可以重新叙述如下:不管程式的輸入源(檔案、管道或終端)和輸出目的地是什麼,程式都應該以同樣的方式工作。這使得在如何使用它方面有最大的靈活性。

指令行參數

  • 指令行參數的定義是:
指令行上除了指令名之外的字元串。參數由多項構成,項與項之間用空白符彼此隔開。參數進一步分為選項和操作數。選項用于修改程式的預設行為或為程式提供資訊,比較老的約定是以短劃線開頭。選項後可以跟随一些參數稱為選項參數,剩下的是操作數。

準則 5. 指令行參數

如果程式可以根據其輸入或使用者的首選參數有不同的行為,則應将它編寫為接受名為 選項的指令行參數,這些參數允許使用者指定什麼行為将用于這個調用。

作為選項的指令行參數由字首“-”(連字元)辨別。另一類參數是那些不是選項的參數,也就是說,它們并不真正更改程式的行為,而更象是資料名稱。通常,這類參數代表程式要處理的檔案名,但也并非一定如此;參數也可以代表其它東西,如列印目的地或作業辨別(有關的示例,請參閱“man cancel”)。

可能代表檔案名或其它任何東西的非選項參數(那些沒有連字元作為字首的)如果出現的話,應該在指令的最後出現。

通常,如果指定了檔案名參數,則程式把它作為輸入。否則程式從标準輸入進行讀取。

所有選項都應以“-”(連字元)開頭。選項可以附加參數。

Linux 實用程式文法圖看起來如下:

$ command mandatory_opts [ optional_opts ] [ other_args ]

其中:

  • command 是指令本身的名稱。
  • mandatory_opts 是為使指令正常工作必須出現的選項清單。
  • optional_opts 是可指定也可不指定的選項清單,這由使用者來選擇;但是,其中一些參數可能是互斥的,如同 selpg 的“-f”和“-l”選項的情況(詳情見下文)。
  • other_args 是指令要處理的其它參數的清單;這可以是任何東西,而不僅僅是檔案名。

在以上定義中,術語“選項清單”是指由空格、跳格或二者的結合所分隔的一系列選項。

以上在方括号中顯示的文法部分可以省去(在此情況下,必須将括号也省去)。

各個選項看起來可能與下面相似:

-f (單個選項)

-s20 (帶附加參數的選項)

-e30 (帶附加參數的選項)

-l66 (帶附加參數的選項)

有些實用程式對帶參數的選項采取略微不同的格式,其中參數與選項由空格分隔 ― 例如,“-s 20” ― 但我沒有選擇這麼做,因為它會使編碼複雜化;這樣做的唯一好處是使指令易讀一些。

以上是 selpg 支援的實際選項。

選項:長格式、短格式

短格式使用“ -”符号(半角減号符)引導開始選項,一般是單個英文字母,字母可以是大寫也可以是小寫。如

$ ls -al
           

用到兩個參數-a -l,是以還可以寫成這樣

$ ls -a -l
           

長格式選項前用“–”(兩個半角減号符)引導開始的,指令選項一般使用英文單詞表示。一般不能組合使用。

IO:stdin、stdout、stderr、管道、重定向

  • stdin、stdout、stderr

    在通常情況下,UNIX每個程式在開始運作的時刻,都會有3個已經打開的stream: stdin, stdout, stderr - 标準 I/O 流。分别用來輸入,輸出,列印診斷和錯誤資訊。

    這3個symbols都是stdio(3) macro,類型為指向FILE的指針。可以被fprintf() fread()等函數使用。

    Linux的本質就是所有都是檔案,輸入輸出裝置也是以檔案形式存在和管理的。

    核心啟動的時候預設打開這三個I/O裝置檔案:标準輸入檔案stdin,标準輸出檔案stdout,标準錯誤輸出檔案stderr,分别得到檔案描述符 0, 1, 2。
  • 重定向

    預設情況下始終有3個"檔案"處于打開狀态,stdin(鍵盤),stdout(螢幕),和stderr(錯誤消息輸出到螢幕上).這3個檔案和其他打開的檔案都可以被重定向。

    對于重定向簡單的解釋就是捕捉一個檔案,指令, 程式,腳本, 或者是腳本中的代碼塊(請參考例子3-1和例子3-2)的輸出,然後将這些輸出作為輸入發送到另一個檔案,指令, 程式,或腳本中。

    我們也可以用<符号來改變标準輸入。

    每個打開的檔案都會被配置設定一個檔案描述符.[1] stdin, stdout,和stderr的檔案描述符分别是0, 1, 和 2. 除了這3個檔案,對于其他那些需要打開的檔案,保留了檔案描述符3到9.在某些情況下,将這些額外的檔案描述符配置設定給stdin,stdout,或stderr作為臨時的副本連結是非常有用的.[2] 在經過複雜的重定向和重新整理之後需要把它們恢複成正常狀态。

  • 管道

    管道可以将一個指令的輸出導向另一個指令的輸入,進而讓兩個(或者更多指令)像流水線一樣連續工作,不斷地處理文本流。

    在指令行中,我們用|表示管道。

環境變量

關于環境變量,這個我們已經在之前的安裝部分詳細講過,這裡就不再贅述了。

關于安裝和環境變量配置的傳送門

Golang之使用Flag和Pflag

Package flag & pflag

作用: Package flag implements command-line flag parsing.

Go語言通過使用标準庫裡的flag包來處理指令行參數。

這裡唯一指的注意的就是傳回值:是指針。

pflag 包與 flag 包的工作原理甚至是代碼實作都是類似的,下面是 pflag 相對 flag 的一些優勢:

  • 支援更加精細的參數類型:例如,flag 隻支援 uint 和 uint64,而 pflag 額外支援 uint8、uint16、int32 等類型。
  • 支援更多參數類型:ip、ip mask、ip net、count、以及所有類型的 slice 類型。
  • 相容标準 flag 庫的 Flag 和 FlagSet:pflag 更像是對 flag 的擴充。
  • 原生支援更豐富的功能:支援 shorthand、deprecated、hidden 等進階功能。

常用函數

  • flag.String(), flag.Bool(), flag.Int(), flag.Float64() 傳回對應類型的指針:

    func Xxx(name string, value Xxx, usage string) *Xxx

  • flag.XxxVar() 将參數綁定到對應類型的指針:

    func XxxVar(p *Xxx, name string, value Xxx, usage string)

  • flag.Var() 綁定自定義類型:

    func Var(value Value, name string, usage string

    自定義類型需要實作Value接口。Var定義了一個有指定名字和用法說明的标簽。标簽的類型和值是由第一個參數指定的,這個參數是Value類型,并且是使用者自定義的實作了Value接口的類型
  • flag.Parse() 解析指令行參數到定義的flag解析函數将會在碰到第一個非flag指令行參數時停止,非flag指令行參數是指不滿足指令行文法的參數,如指令行參數為./selpg -s1 -e2 input.txt 則第一個非flag指令行參數為“input.txt”。
  • flag.Args(),flag.Arg(i),flag.NArg()

    在指令行标簽被解析之後(遇到第一個非flag參數),flag.NArg()就傳回解析後參數的個數。

  • flag.Usage() 輸出指令行的提示資訊

3、開發實踐

使用 golang 開發 開發 Linux 指令行實用程式 中的 selpg

提示:

  • 請按文檔 使用 selpg 章節要求測試你的程式
  • 請使用 pflag 替代 goflag 以滿足 Unix 指令行規範, 參考:Golang之使用Flag和Pflag
  • golang 檔案讀寫、讀環境變量,請自己查 os 包
  • “-dXXX” 實作,請自己查 os/exec 庫,例如案例 Command,管理子程序的标準輸入和輸出通常使用 io.Pipe,具體案例見 Pipe

slepg程式邏輯及源代碼了解

  • selpg 是從文本輸入選擇頁範圍的實用程式。該輸入可以來自作為最後一個指令行參數指定的檔案,在沒有給出檔案名參數時也可以來自标準輸入。
  • selpg 首先處理所有的指令行參數。在掃描了所有的選項參數(也就是那些以連字元為字首的參數)後,如果 selpg 發現還有一個參數,則它會接受該參數為輸入檔案的名稱并嘗試打開它以進行讀取。如果沒有其它參數,則 selpg 假定輸入來自标準輸入。

參數處理

  • 強制選項:“-sNumber”和“-eNumber”

    selpg 要求使用者用兩個指令行參數“-sNumber”(例如,“-s10”表示從第 10 頁開始)和“-eNumber”(例如,“-e20”表示在第 20 頁結束)指定要抽取的頁面範圍的起始頁和結束頁。

  • 可選選項:“-lNumber”和“-f”

    selpg 可以處理兩種輸入文本:

    • 文本的頁行數固定。這是預設類型,如果既沒有給出“-lNumber”也沒有給出“-f”選項,則 selpg 會了解為頁有固定的長度(在我的程式中預設每頁 20 行)。

      該預設值可以用“-lNumber”選項覆寫,如下所示:

    • 該類型文本的頁由 ASCII 換頁字元(十進制數值為 12,在 C 中用“\f”表示)定界。在含有文本的行後面,隻需要一個字元 ― 換頁 ― 就可以表示該頁的結束。
    注:“-lNumber”和“-f”選項是互斥的。
  • 可選選項:“-dDestination”

    selpg 還允許使用者使用“-dDestination”選項将標明的頁直接發送至列印機。

    $ selpg -s10 -e20 -dlp1
               

代碼實作

首先安裝spf13/pflag 包,以調用flag和pflag:

$ go get github.com/spf13/pflag
           

設定程式的參數結構體。提取參數将值指派給該結構體:

type  selpg_args struct {
	start_page int  //開始頁
	end_page int //結束頁
	in_filename string  // 輸入檔案名 
	print_dest string	//輸出檔案名 
	page_len int  // 每頁的行數
	page_type string  // 'l'按行列印,'f'按換頁符列印,預設按行
}
           

main函數

和原函數基本一緻,由于我們使用pflag綁定了sa的各個變量,可以省略掉一些初始化。

func main() {
sa := sp_args{}
	progname = os.Args[0]
// 處理參數 
process_args(&sa)
// 處理輸入輸出 
process_input(sa)
}
           

func process_args函數

在這個函數中首先對指令行的參數進行參數值的綁定,通過 pflag.Parse()方法讓pflag 對辨別和參數進行解析。之後就可以直接使用綁定的值。

使用os.Args讀取程式輸入的所有參數,并進行合法性檢驗,包括對每個參數的格式是否正确進行判斷,對參數的個數是否正确進行判斷,還有參數大小是否在合法範圍内進行判斷等等。得到的值是包含參數的string數組,然後将參數的值提取出來指派給結構體。

func process_args(sa * sp_args) {
//将flag綁定到sa的各個變量上 
	flag.IntVarP(&sa.start_page,"start",  "s", -1, "start page(>1)")
	flag.IntVarP(&sa.end_page,"end", "e",  -1, "end page(>=start_page)")
	flag.IntVarP(&sa.page_len,"len", "l", 72, "page len")
	flag.StringVarP(&sa.print_dest,"dest", "d", "", "print dest")
	flag.StringVarP(&sa.page_type,"type", "f", "l", "'l' for lines-delimited, 'f' for form-feed-delimited. default is 'l'")
	flag.Lookup("type").NoOptDefVal = "f"
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
"USAGE: \n%s -s start_page -e end_page [ -f | -l lines_per_page ]" + 
" [ -d dest ] [ in_filename ]\n", progname)
		flag.PrintDefaults()
	}
	flag.Parse()

// os.Args是一個儲存了所有參數的string數組,我們可以使用下标來通路參數 
// if參數個數不夠
if len(os.Args) < 3 {	
		fmt.Fprintf(os.Stderr, "\n%s: not enough arguments\n", progname)
		flag.Usage()
		os.Exit(1)
	}
// 處理第一個參數 - start page 
// if第一個參數不為's'或數值不在合法範圍内
if os.Args[1] != "-s" {
		fmt.Fprintf(os.Stderr, "\n%s: 1st arg should be -s start_page\n", progname)
		flag.Usage()
		os.Exit(2)
	}
INT_MAX := 1 << 32 - 1
if(sa.start_page < 1 || sa.start_page > INT_MAX) {
		fmt.Fprintf(os.Stderr, "\n%s: invalid start page %s\n", progname, os.Args[2])
		flag.Usage()
		os.Exit(3)
	}
// 處理第二個參數 - end page 
//if第二個參數不為'e'
if os.Args[3] != "-e" {
		fmt.Fprintf(os.Stderr, "\n%s: 2nd arg should be -e end_page\n", progname)
		flag.Usage()
		os.Exit(4)
	}
//if end_page 數值不在合法範圍内,且小于等于start_page
if sa.end_page < 1 || sa.end_page > INT_MAX || sa.end_page < sa.start_page {
		fmt.Fprintf(os.Stderr, "\n%s: invalid end page %s\n", progname, sa.end_page)
		flag.Usage()
		os.Exit(5)
	}
// 處理page_len 
if ( sa.page_len < 1 || sa.page_len > (INT_MAX - 1) ) {
		fmt.Fprintf(os.Stderr, "\n%s: invalid page length %s\n", progname, sa.page_len)
		flag.Usage()
		os.Exit(5)
	}
// 設定in_filename  
//檢查是否還有剩餘的參數。對于 selpg,最多有一個這樣的參數,它被用作輸入的檔案名。
if len(flag.Args()) == 1 {
_, err := os.Stat(flag.Args()[0])
// 檢查檔案是否存在 
if err != nil && os.IsNotExist(err) {
			fmt.Fprintf(os.Stderr, "\n%s: input file \"%s\" does not exist\n",
					progname, flag.Args()[0]);
			os.Exit(6);
		}
		sa.in_filename = flag.Args()[0]
	}
/* page_len */ 
}

           

func process_input函數

和原函數類似的,我們先選擇從哪裡讀取和在哪兒列印,接着按照page_type進行列印。當使用者指定了輸出地點時,我們通過cmd建立子程式“cat”, 幫助我們将輸出流的内容列印到指定地點。

func process_input(sa sp_args) {
// 輸入流 
var fin *os.File 
// 設定輸入流。輸入可以來自終端(使用者鍵盤),檔案或另一個程式的輸出
if len(sa.in_filename) == 0 {
		fin = os.Stdin
	} else {
var err error
		fin, err = os.Open(sa.in_filename)
if err != nil {
			fmt.Fprintf(os.Stderr, "\n%s: could not open input file \"%s\"\n",
				progname, sa.in_filename)
			os.Exit(7)
		}
defer fin.Close()
	}
//使用 bufio.NewReader 來獲得一個讀取器變量
bufFin := bufio.NewReader(fin)
// 設定輸出地點。輸出可以是螢幕,檔案或另一個檔案的輸入 
var fout io.WriteCloser
cmd := &exec.Cmd{}
if len(sa.print_dest) == 0 {
		fout = os.Stdout
	} else {
		cmd = exec.Command("cat")
//用隻寫的方式打開 print_dest 檔案,如果檔案不存在,就建立該檔案。 
var err error
		cmd.Stdout, err = os.OpenFile(sa.print_dest, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
			fmt.Fprintf(os.Stderr, "\n%s: could not open file %s\n",
				progname, sa.print_dest)
			os.Exit(8)
		}
//StdinPipe傳回一個連接配接到command标準輸入的管道pipe 
		fout, err = cmd.StdinPipe()
if err != nil {
			fmt.Fprintf(os.Stderr, "\n%s: could not open pipe to \"lp -d%s\"\n",
				progname, sa.print_dest)
			os.Exit(8)
		}
		cmd.Start()
defer fout.Close()
	}
//根據page_type(按固定行數或分頁符進行列印) 
//目前頁數 
var page_ctr int
if sa.page_type == "l" { //按固定行數列印 
line_ctr := 0
		page_ctr = 1
for {
//上文寫到的bufFin := bufio.NewReader(fin)
line,  crc := bufFin.ReadString('\n')
if crc != nil {
break // 碰到eof 
			}
			line_ctr++
if line_ctr > sa.page_len {
				page_ctr++
				line_ctr = 1
			}
//到達指定頁碼,開始列印 
if (page_ctr >= sa.start_page) && (page_ctr <= sa.end_page) {
_, err := fout.Write([]byte(line))
if err != nil {
					fmt.Println(err)
					os.Exit(9)
				}
		 	}
		}  
	} else {			//按分頁符列印 
		page_ctr = 1
for {
page, err := bufFin.ReadString('\n')
//txt 沒有換頁符,使用\n代替,而且便于測試
//line, crc := bufFin.ReadString('\f')
if err != nil {
break // eof
			}
//到達指定頁碼,開始列印
if (page_ctr >= sa.start_page) && (page_ctr <= sa.end_page) {
_, err := fout.Write([]byte(page))
if err != nil {
					os.Exit(5)
				}
			}
//每碰到一個換頁符都增加一頁 
			page_ctr++
		}
	}
//if err := cmd.Wait(); err != nil {
//handle err
if page_ctr < sa.start_page {
			fmt.Fprintf(os.Stderr,
"\n%s: start_page (%d) greater than total pages (%d)," +
" no output written\n", progname, sa.start_page, page_ctr)
		} else if page_ctr < sa.end_page {
			fmt.Fprintf(os.Stderr,"\n%s: end_page (%d) greater than total pages (%d)," +
" less output than expected\n", progname, sa.end_page, page_ctr)
		} 
}
           

測試運作

指令行格式如下:

$ selpg -s startPage -e endPage [-l linePerPage | -f ][-d dest] filename
           
其中,-s表示開始列印的頁碼,-e表示結束列印的頁碼,這兩個必須寫上; 而-l表示按固定行數列印檔案,-f表示按照換頁符來列印,預設按行;-d則是列印的目的地,預設為螢幕。

按照老師的要求,我們在這裡使用開發 Linux 指令行實用程式上面的測試用例

$ selpg -s 1 -e 1 test.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

該指令将把“input_file”的第 1 頁寫至标準輸出(也就是螢幕),因為這裡沒有重定向或管道。

在代碼中我設定的預設一頁有10行,是以這裡輸出到line10。

$ selpg -s 1 -e 2 <test.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

該指令與示例 1 所做的工作相同,但在本例中,selpg 讀取标準輸入,而标準輸入已被 shell/核心重定向為來自“input_file”而不是顯式命名的檔案名參數。輸入的前2 頁被寫至螢幕。

Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

“other_command”的标準輸出被 shell/核心重定向至 selpg 的标準輸入。将第 2 頁到第 2 頁寫至 selpg 的标準輸出(螢幕)。

$ selpg -s 1 -e 2 test.txt >output.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

selpg 将第 1 頁到第 2 頁寫至标準輸出;标準輸出被 shell/核心重定向至“output_file”。

$ selpg -s 0 -e 2 test.txt >error.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

selpg 将第 0 頁到第 2 頁寫至标準輸出(螢幕);所有的錯誤消息被 shell/核心重定向至“error_file”。

這裡因為第0頁是一個不合法的參數,是以會報錯,并且顯示help

$ selpg -s 1 -e 2 test.txt 2>error.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

和上一條類似,請注意:在“2”和“>”之間不能有空格;這是 shell 文法的一部分(請參閱“man bash”或“man sh”)。

$ selpg -s 1 -e 2 test.txt >output.txt 2>error.txt
$ selpg -s 1 -e 2 test.txt >output.txt 2>/dev/null
$ selpg -s 1 -e 1 test.txt >/dev/null
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐
  • 第一條語句,selpg 将第 10頁到第 20頁寫至标準輸出,标準輸出被重定向至“output_file”;selpg 寫至标準錯誤的所有内容都被重定向至“error_file”。當“input_file”很大時可使用這種調用;這種方法可對輸出和錯誤都進行儲存。
  • 第二條語句,selpg 将第 10頁到第 2 頁寫至标準輸出,标準輸出被重定向至“output_file”;selpg 寫至标準錯誤的所有内容都被重定向至 /dev/null(空裝置),這意味着錯誤消息被丢棄了。裝置檔案 /dev/null 廢棄所有寫至它的輸出,當從該裝置檔案讀取時,會立即傳回 EOF。即不儲存錯誤。
  • 第三條語句,selpg 将第 1 頁到第 1 頁寫至标準輸出,标準輸出被丢棄;錯誤消息在螢幕出現。這可作為測試 selpg 的用途,此時您也許隻想(對一些測試情況)檢查錯誤消息,而不想看到正常輸出。
$ selpg -s 1 -e 1 test.txt | wc
$ selpg -s 1 -e 1 test.txt 2>error.txt | wc
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐
  • 第一條語句,selpg 的标準輸出透明地被 shell/核心重定向,成為“other_command”的标準輸入,第 1 頁到第 2 頁被寫至該标準輸入。“other_command”的示例是 wc,它會顯示標明範圍的頁中包含的行數、字數和字元數。錯誤消息仍在螢幕顯示。
  • 與上一條語句相似,隻有一點不同:錯誤消息被寫至“error_file”。
$ selpg -s 1 -e 1 -l 1 test.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

該指令将頁長設定為 1 行,這樣 selpg 就可以把輸入當作被定界為該長度的頁那樣處理。第 101頁到第 1頁被寫至 selpg 的标準輸出(螢幕)。可以看到本來一頁是十個line,但是設定完-l參數以後,一頁變成隻有一行。

$ selpg -s 1 -e 1 -f test.txt
           
Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

假定頁由換頁符定界。第 10頁到第 1頁被寫至 selpg 的标準輸出(螢幕)。

Sevice Computing服務計算:CLI 指令行實用程式開發基礎--selpg1、概述2、基礎知識3、開發實踐

該指令利用了 Linux 的一個強大特性,即:在“背景”運作程序的能力。在這個例子中發生的情況是:“程序辨別”(pid)如 1234 将被顯示,然後 shell 提示符幾乎立刻會出現,使得您能向 shell 輸入更多指令。同時,selpg 程序在背景運作,并且标準輸出和标準錯誤都被重定向至檔案。這樣做的好處是您可以在 selpg 運作時繼續做其它工作。

Github代碼傳送門