Sevice Computing服務計算:CLI 命令行實用程序開發基礎--selpg

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 頁結束)指定要抽取的頁面範圍的起始頁和結束頁。
    $ selpg -s10 -e20 ...
    
  • 可選選項:“-lNumber”和“-f”
    selpg 可以處理兩種輸入文本:
    • 文本的頁行數固定。這是缺省類型,如果既沒有給出“-lNumber”也沒有給出“-f”選項,則 selpg 會理解爲頁有固定的長度(在我的程序中默認每頁 20 行)。
      該缺省值可以用“-lNumber”選項覆蓋,如下所示:
    $ selpg -s10 -e20 -l66 ...
    
    • 該類型文本的頁由 ASCII 換頁字符(十進制數值爲 12,在 C 中用“\f”表示)定界。在含有文本的行後面,只需要一個字符 ― 換頁 ― 就可以表示該頁的結束。
    $ selpg -s10 -e20 -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

在這裏插入圖片描述
該命令將把“input_file”的第 1 頁寫至標準輸出(也就是屏幕),因爲這裏沒有重定向或管道。
在代碼中我設定的默認一頁有10行,所以這裏輸出到line10。

$ selpg -s 1 -e 2 <test.txt

在這裏插入圖片描述
該命令與示例 1 所做的工作相同,但在本例中,selpg 讀取標準輸入,而標準輸入已被 shell/內核重定向爲來自“input_file”而不是顯式命名的文件名參數。輸入的前2 頁被寫至屏幕。

$ cat test.txt | selpg -s 2 -e 2

在這裏插入圖片描述
“other_command”的標準輸出被 shell/內核重定向至 selpg 的標準輸入。將第 2 頁到第 2 頁寫至 selpg 的標準輸出(屏幕)。

$ selpg -s 1 -e 2 test.txt >output.txt

在這裏插入圖片描述
在這裏插入圖片描述
selpg 將第 1 頁到第 2 頁寫至標準輸出;標準輸出被 shell/內核重定向至“output_file”。

$ selpg -s 0 -e 2 test.txt >error.txt

在這裏插入圖片描述
selpg 將第 0 頁到第 2 頁寫至標準輸出(屏幕);所有的錯誤消息被 shell/內核重定向至“error_file”。
這裏因爲第0頁是一個不合法的參數,所以會報錯,並且顯示help

$ selpg -s 1 -e 2 test.txt 2>error.txt

在這裏插入圖片描述
和上一條類似,請注意:在“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

在這裏插入圖片描述

  • 第一條語句,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

在這裏插入圖片描述

  • 第一條語句,selpg 的標準輸出透明地被 shell/內核重定向,成爲“other_command”的標準輸入,第 1 頁到第 2 頁被寫至該標準輸入。“other_command”的示例是 wc,它會顯示選定範圍的頁中包含的行數、字數和字符數。錯誤消息仍在屏幕顯示。
  • 與上一條語句相似,只有一點不同:錯誤消息被寫至“error_file”。
$ selpg -s 1 -e 1 -l 1 test.txt

在這裏插入圖片描述
該命令將頁長設置爲 1 行,這樣 selpg 就可以把輸入當作被定界爲該長度的頁那樣處理。第 101頁到第 1頁被寫至 selpg 的標準輸出(屏幕)。可以看到本來一頁是十個line,但是設置完-l參數以後,一頁變成只有一行。

$ selpg -s 1 -e 1 -f test.txt

在這裏插入圖片描述
假定頁由換頁符定界。第 10頁到第 1頁被寫至 selpg 的標準輸出(屏幕)。

$ selpg -s 1 -e 1 test.txt > output.txt 2>error.txt &

在這裏插入圖片描述
該命令利用了 Linux 的一個強大特性,即:在“後臺”運行進程的能力。在這個例子中發生的情況是:“進程標識”(pid)如 1234 將被顯示,然後 shell 提示符幾乎立刻會出現,使得您能向 shell 輸入更多命令。同時,selpg 進程在後臺運行,並且標準輸出和標準錯誤都被重定向至文件。這樣做的好處是您可以在 selpg 運行時繼續做其它工作。

Github代碼傳送門

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章