跳轉到內容

Unix 指南/解釋/環境

來自華夏公益教科書

環境變數

[編輯 | 編輯原始碼]

每個程式都從啟動它的程式繼承“環境變數”。這意味著每個程式都具有與啟動它的程式相同的環境變數。程式的環境可以改變,在這種情況下,該程式後來啟動的其他程式也具有更改的環境。

名為SHELL的環境變數包含預設 shell 的名稱。(您可能正在執行其他 shell,但預設 shell 始終在“SHELL”中。)“SHELL”環境變數在登入時設定,並由 shell 繼承。當我們啟動“printenv”程式時,它繼承了整個環境,包括“SHELL”,並將“SHELL”顯示給我們。

在沒有引數的情況下使用printenv會給出整個環境

$ printenv
...
SHELL=/bin/bash
...

參見環境變數,以瞭解有關特定變數的資訊。

程序限制

[編輯 | 編輯原始碼]

每個程序對以下內容都有限制。

限制是從父程序繼承的。設定限制的正常方法是更改某些 shell 的限制,然後從該 shell 啟動程式。每個 shell 都提供不同的設定限制方法:Bourne shell 使用ulimit命令,而 C shell 使用limit


以下是可用限制的列表,並給出它們的C shell 名稱(“Bourne shell 選項”)。我們需要名稱(“選項”)來在C(“Bourne”)shell 中顯示或更改限制。

* cputime ("-t SECONDS")
* filesize ("-f BLOCKS")
* datasize ("-n KILOBYTES")
* stacksize ("-s KILOBYTES")
* coredumpsize ("-c BLOCKS") can also accept "unlimited" instead of BLOCKS
* memoryuse ("-m KILOBYTES")
* memorylocked ("-l KILOBYTES")
* maxproc ("-p COUNT")
* openfiles ("-n COUNT")


Clipboard

待辦事項


場景:程式崩潰並顯示有關“資源暫時不可用”或“開啟的檔案過多”的訊息。


從啟動程式的 shell 中提高限制。例如,要允許每個程序開啟 512 個檔案

Bourne shell
$ ulimit -Sn 512
C shell
$ limit openfiles 512

Shell 引數

[編輯 | 編輯原始碼]

可以透過 shell 的引數訪問 shell 的環境。引數有三種類型

  • 命名引數
  • 位置引數
  • 特殊引數

命名引數被稱為環境變數。其中一些變數在使用者登入時初始化,shell 使用它們來確定其行為。shell 還將在整個登入會話中維護某些變數,其中一些變數由 shell 定義並具有特殊含義,而另一些變數可以由使用者定義。

位置引數允許引用提供給 shell 指令碼的引數。shell 透過特殊引數傳達程序資訊。使用者可以訪問特殊引數,但與命名引數和位置引數不同,它們只能由 shell 本身設定。

環境變數是命名引數。變數使用命令列語句以name=value的形式分配。變數名稱是字母數字字串,可以包含數字字元或下劃線,但必須以字母字元開頭。下面列出了四個示例

$ n=2 
$ n2=4
$ N=3
$ MYNAME=alan 
$ greeting="hello world"

請注意,Unix 區分大小寫,因此變數“name”與“Name”不同。可以透過在變數名前加 $ 來擴充套件(求值)變數,例如

$ echo $greeting
hello world

變數可以連線起來

$ day=21 month=01 year=2006
$ echo $day$month$year
21012006

但是,將文字與變數連線起來可能會出現問題,使用變量表示日期和月份,但對年份進行硬編碼會導致

$ echo $day$month2006
05

問題是 shell 無法區分變數名month和字面值 2006。因此,它嘗試擴充套件一個變數,該變數的變數名為month2006,該變數未設定。可以透過用大括號 {} 括起變數名來解決此問題,例如

$ echo ${day}${month}2006
05112006

雖然並不總是需要大括號,但在求值變數時使用大括號是一種很好的做法。

考慮以下示例,其中變數擴充套件短語用雙引號括起來

$ echo "echo $greeting", displays $greeting
echo hello world, displays hello world

用雙引號括起來的變數仍然由 shell 擴充套件。要防止擴充套件,變數應使用單引號括起來。要按字面意思顯示字串“echo $greeting”,請使用以下語句

$ echo ’echo $greeting’, displays $greeting
echo $greeting, displays hello world

更多引數擴充套件

[編輯 | 編輯原始碼]

shell 支援相當複雜的引數擴充套件。結構NAME-literal如果引數已設定,則擴充套件為引數的值,否則擴充套件為文字值。例如

$ echo ${OPSYS-unix}    
unix

變數OPSYS尚未分配值,因此命令的結果是在螢幕上回顯字串“unix”。如果變數被分配一個值

$ OPSYS=Linux
$ echo ${OPSYS-unix}
Linux

如果變數設定為 NULL,則顯示空白連結而不是文字值

$ OPSYS=""        
$ echo ${OPSYS-unix}  
(blank line)

但是,如果我們使用name:-literal,則變數將求值為文字而不是其 NULL 值

$ echo ${OPSYS:-unix}
unix

使用結構name=literal,如果引數尚未設定,則將引數分配為文字的值。假設變數 DAY 以前未設定

$ echo $DAY
(blank line)

則以下命令列將字串“tuesday”分配給變數DAY並將其回顯到螢幕

$ echo ${DAY=tuesday}
tuesday

現在變數DAY已設定,因此如果命令列用不同的文字值重複,則其值保持不變

$ echo ${DAY=wednesday}
tuesday

如果DAY分配為 NULL 值,則以下引數擴充套件將導致返回 NULL 值

$ DAY=""
$ echo ${DAY=tuesday}
(blank line)

一個相關的擴充套件結構是name-value,它只在引數未分配時返回文字,即使值為 NULL,它也返回分配的值。

使用擴充套件結構name:=literal,如果引數尚未設定,則將引數分配為文字的值。例如

$ echo $DISTRO               # DISTRO not assigned
echo ${DISTRO:=ubuntu}
ubuntu

此外,如果引數為 NULL,則分配文字,因此擴充套件結果為

$ DISTRO=""
$ echo ${DISTO:=ubuntu}
ubuntu

如果引數設定為非 NULL 值,則不會分配文字

$ echo ${DISTRO:=debian}
ubuntu

擴充套件結構name=value類似於name:=value,但是僅在引數以前未分配時才分配文字。如果引數值為 NULL,則不會分配文字。使用name:?messagestring結構,如果引數未設定或為 NULL,則可以顯示錯誤訊息

$ echo ${DISTVER:?distribution version unknown}
-bash: DISTVER: distribution version unknown
$ DISTVER=""
$ echo ${DISTVER:?distribution version unknown}
-bash: DISTVER: distribution version unknown

如果引數分配了非 NULL 值,則不會顯示error訊息

$ DISTVER="hoary hedgehog"
$ echo ${DISTVER:?distribution version not set}
hoary hedgehog

同樣,還有一個相關的結構name?messagestring,它僅在引數未設定時返回訊息字串。可以將引數擴充套件為其值的子字串。擴充套件結構name%patternname%%pattern分別擴充套件為模式左側的最大和最小子字串。例如,設定變數TIME

$ TIME=10:29:39

以下命令列僅顯示小時值

$ echo the hours is ${TIME%%:*}
the hours is 10

而以下命令列則顯示小時和分鐘

$ echo the hour and minutes are ${TIME%:*}
the hour and minutes are 10:29

結構name#patternname##pattern分別擴充套件為模式右側的最大和最小子字串。因此,以下命令列擴充套件為秒

$ echo the seconds are ${TIME##*:}
the seconds are 39

分鐘和秒可以用以下命令提取

$ echo the minutes and seconds are ${TIME#*:}
the minutes and seconds are 29:39

可以使用#name結構求解字串的長度。例如

$ echo ${#TIME}
8

變數可以是一維陣列,並以name[index]的形式引用。可以使用name=(value1 value2 ... )結構分配陣列,例如

DATE=($(date))

$(command)擴充套件為命令列上command的結果。因此,DATE被分配為命令date的輸出,其中每個空格分隔的字串都被分配給陣列中的一個元素。因此,如果我們只想要年份,那麼我們將擴充套件陣列的第六個元素

$ echo ${DATE[5]}
2006

我們可以使用以下分配來更改時間資訊,例如

DATE[3]=19:16:00
$ echo ${DATE[*]}
Wed May 17 19:16:00 NZST 2006

如果我們需要統計已分配陣列元素的數量,那麼我們將使用以下表達式

$ echo ${#DATE[*]}
6

位置引數

[編輯 | 編輯原始碼]

位置引數提供了一種訪問 shell 指令碼引數的方法。它們由數字 1、2、3、... 等引用,其中數字反映了引數在命令列中出現的順序(從左到右)。它們不能像命名引數那樣分配,也就是說,您不能這樣做:1=hello。當 shell 指令碼使用命令列引數呼叫時,位置引數被分配。但是,它們可以使用 set 命令分配

$ set $(uname -rmv)

這將導致uname -rmv的輸出中空格分隔的字串被分配給位置引數。以下語句顯示了所有分配的位置引數的值

$ echo ${*}
2.6.14.4 #1 SMP Thu Dec 22 01:04:35 NZDT 2005 x86_64

以下語句給出分配的位置引數的數量(相當於 C 和 C++ 中的 argc)

$ echo $#
10

如果我們特別想要核心版本,那麼我們將擴充套件位置引數 1

$ echo $1
2.6.14.4

兩位數位置引數需要用大括號括起來。因此,如果我們想要機器的體系結構,我們不能這樣做

$ echo $10
2.6.14.40

上面的表示式擴充套件為位置引數 1 後面跟一個文字 0。正確的方法是

$ echo ${10}
x86_64

最後,位置引數 0 被設定為當前程序的名稱(相當於 argv[0],在本例中應該是 shell 命令本身的名稱)

$ echo $0
-bash

特殊引數

[編輯 | 編輯原始碼]

使用者可以訪問特殊引數,但只能由 shell(通常在響應某些事件時)直接賦值。我們已經遇到了引數 * 和 #,它們在命令使用引數(或使用 set 命令)呼叫時被設定。

?引數擴充套件為最後退出程序的返回值。如果我們將命令列語句括在方括號中,它將在子 shell 中執行。以下命令列會派生一個新的 shell 並立即發出 exit 命令。exit 後面的引數是子 shell 的返回值(在本例中為 255)

$ (exit 255)

父程序可以透過擴充套件?來訪問子程序的返回值。如果程序正常終止,則返回值可能為零。但是,如果程序由於某種錯誤條件而終止,則如果程序設定了一個非零返回值,那麼父程序就可以確定終止的原因,這將非常有用。

$ echo $?
255

shell 的程序 ID 儲存在 $ 中。ps 命令顯示我的 shell 的程序 ID 為 10005,這與 $ 的擴充套件結果一致。

$ ps | grep bash
10005 pts/77            00:00:00 bash
$ echo $$
10005

!引數計算為最後一個後臺程序的程序 ID。為了演示,以下語句在後臺呼叫 sleep 命令(睡眠 60 秒)

$ sleep 60 &
[1] 24835

後臺程序的程序 ID 會顯示在螢幕上(在本例中為 24835)。!引數的擴充套件確認了這一點。

$ echo $!
24835

匯出引數

[編輯 | 編輯原始碼]

程序不會從其父程序繼承環境變數,除非變數被匯出。以下命令列顯示當前程序的程序 ID,然後是 greeting 的值(之前已賦值)

$ echo ${greeting?parameter not set}
hello world

如果我們啟動一個新的 shell 並再次發出命令,我們會看到 greeting 在子程序中沒有設定。

$ bash
$ echo ${greeting?parameter not set}
bash: greeting: parameter not set

終止當前 shell 程序並返回到父程序,然後匯出 greeting。

$ exit
$ export greeting

您可以透過鍵入以下內容來確認變數是否設定了 export 屬性。

$ export | grep greeting
declare -x greeting="hello world"

現在再次啟動一個新的 shell 並評估 greeting。

$ bash
$ echo ${greeting?parameter not set}
hello world

新的 shell 繼承了變數 greeting 並且可以對其進行評估。即使變數被重新賦值,它也會保留其 export 屬性(直到 shell 終止),但可以使用 typeset 命令將其刪除。

$ typeset +x greeting
$ export | grep greeting     # yields no result

在 Bash(以及 Kornshell,但不是 Bourne shell)中,可以在一個命令列語句中分配和匯出變數。

$ export greeting="hi there"
$ export | grep greeting
declare -x greeting="hi there"


華夏公益教科書