Skip to content

Latest commit

 

History

History
417 lines (289 loc) · 14.1 KB

File metadata and controls

417 lines (289 loc) · 14.1 KB

4.4. Special Variable Types

第四章第四節:特殊的變數型態

Local variables

區域變數

Variables visible only within a code block or function (see also local variables in functions)

只能在某一程式區塊或函式內才有效用的變數。 (詳情請參閱:第二十四章 "函式" 的第二節 "區域變數"。)

Environmental variables

環境變數

Variables that affect the behavior of the shell and user interface

能夠影響 shell 的行為與使用者界面的變數。

In a more general context, each process has an "environment", that is, a group of variables that the process may reference.

一般來說,每個程序皆有一個 "環境",也就是該程序可能會參考到的一組變數。

In this sense, the shell behaves like any other process.

在這樣的觀念上,shell 呈現出跟其他任何程序一樣的行為。

Every time a shell starts, it creates shell variables that correspond to its own environmental variables.

在每次啟動一個 shell 時,它都會建立對應它所擁有的環境變數之 shell 變數。

Updating or adding new environmental variables causes the shell to update its environment, and all the shell's child processes (the commands it executes) inherit this environment.

更新或新增環境變數會使 shell 更新自已以及繼承此環境的所有子程序 (包含由它所執行來出的指令) 之相對應的 shell 變數。

Note: The space allotted to the environment is limited.

注意:可配置給環境變數的空間有限。

Creating too many environmental variables or ones that use up excessive space may cause problems.

建立太多或太大的環境變數可能會出問題。

bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"
bash$ du
bash: /usr/bin/du: Argument list too long

Note: this "error" has been fixed, as of kernel version 2.6.23.

注意:這個 "錯誤" 已經在 kernel 2.6.23 版本之後修正了。

(Thank you, Stéphane Chazelas for the clarification, and for providing the above example.)

(感謝 Stéphane Chazelas 釐清此問題並提供上述檢測用的範例指令。)

If a script sets environmental variables, they need to be "exported," that is, reported to the environment local to the script.

若一個腳本設定了環境變數,則這些變數必需被 "exported",亦即回報至該腳本所屬 "環境"。

This is the function of the export command.

(export 指令詳情請參閱:第十五章 "內建指令" 的 "export" 區段。)

A script can export variables only to child processes, that is, only to commands or processes which that particular script initiates.

一個腳本僅可以 "export" 變數到子程序,也就是僅限於此腳本所喚起的指令或程序。

A script invoked from the command-line cannot export variables back to the command-line environment.

由命令列呼叫起來的某一腳本是 "無法" 將變數 export 回命令列環境的。

Child processes cannot export variables back to the parent processes that spawned them.

子程序無法將變數 export 回的孵育出它們的父程序。

Definition: A child process is a subprocess launched by another process, its parent.

定義:子程序 (child process) 為由另一個程序,也就是父程序,所啟動的子程序 (subprocess)。

Positional parameters

位置參數

Arguments passed to the script from the command line [26] : $0, $1, $2, $3 . . .

從命令列傳給腳本的引數 [26] : $0, $1, $2, $3 . . .

$0 is the name of the script itself, $1 is the first argument, $2 the second, $3 the third, and so forth. [27]

$0 為腳本本身的檔名,$1 為第一個引數,$2 為第二個,$3 第三,以此類推。 [27]

After $9, the arguments must be enclosed in brackets, for example, ${10}, ${11}, ${12}.

編號大於 $9 之後,引數名稱必須用大括號包起來,例如: ${10}, ${11}, ${12}。

The special variables $* and $@ denote all the positional parameters.

特殊變數 $* 與 $@ 為代表所有的位置參數。

Example 4-5. Positional Parameters

範例 4-5. 位置參數

#!/bin/bash

# Call this script with at least 10 parameters, for example
# 以至少帶 10 個引數的方式呼叫此腳本,例如:
# ./scriptname 1 2 3 4 5 6 7 8 9 10
MINPARAMS=10

echo

echo "The name of this script is \"$0\"."
# Adds ./ for current directory
# 會附帶路徑 ./ 以表示是當前目錄
echo "The name of this script is \"`basename $0`\"."
# Strips out path name info (see 'basename')
# 去掉路徑部份後的資訊 (詳情請參閱: 'basename' 指令)

echo

if [ -n "$1" ]                 # Tested variable is quoted.
                               # 待測變數有用雙引號包住。
then
  echo "Parameter #1 is $1"    # Need quotes to escape #
                               # 井號 (#) 需要引號包住以跳脫原本的註解效果。
fi

if [ -n "$2" ]
then
  echo "Parameter #2 is $2"
fi

if [ -n "$3" ]
then
  echo "Parameter #3 is $3"
fi

# ...


if [ -n "${10}" ]              # Parameters > $9 must be enclosed in {brackets}.
                               # 大於 $9 以後的參數必須使用大括號包住變數名稱。
then
  echo "Parameter #10 is ${10}"
fi

echo "-----------------------------------"
echo "All the command-line parameters are: "$*""

if [ $# -lt "$MINPARAMS" ]
then
  echo
  echo "This script needs at least $MINPARAMS command-line arguments!"
fi

echo

exit 0

Bracket notation for positional parameters leads to a fairly simple way of referencing the last argument passed to a script on the command-line.

"括號標記" 於位置參數可以兜出一個相當簡單的方式來參考到從命令列傳進腳本的 "最後一個" 引數。

This also requires indirect referencing.

而且這也會用到間接參考。

args=$#           # Number of args passed.
                  # 帶入的引數個數。
lastarg=${!args}
# Note: This is an *indirect reference* to $args ...
# 注意:這是一個對 $args 參數的 "間接參考"


# Or:       lastarg=${!#}             (Thanks, Chris Monson.)
# 或:       lastarg=${!#}             (感謝 Chris Monson.)
# This is an *indirect reference* to the $# variable.
# 這是一個對 $# 變數的 "間接參考"。
# Note that lastarg=${!$#} doesn't work.
# 但請注意:像 lastarg=${!$#} 這樣是行不通的。

Some scripts can perform different operations, depending on which name they are invoked with.

有些腳本可以依據呼叫它們的名稱之不同而有不同的運行效果。

For this to work, the script needs to check $0, the name it was invoked by. [28]

要達到這樣的效果,腳本必須去檢查喚起它的名子於引數 $0。 [28]

There must also exist symbolic links to all the alternate names of the script. See Example 16-2.

並且必須還要有存在指向此腳本的所有別名之符號連結 (symbolic links)。詳情請參閱:範例 16-2.

If a script expects a command-line parameter but is invoked without one, this may cause a null variable assignment, generally an undesirable result.

若腳本預期要有引數從命令列輸入,但呼叫時並沒有帶入,則可能會造成賦值空字串變數,這通常不是預期的結果。

One way to prevent this is to append an extra character to both sides of the assignment statement using the expected positional parameter.

為了預防這一點,有一種方法就是多加一個多餘的字元到位置參數的變數賦值敘述之兩側。

variable1_=$1_  # Rather than variable1=$1
                # 而不是直接地 variable1=$1 的方式賦值
# This will prevent an error, even if positional parameter is absent.
# 這可以預防錯誤,甚至是缺乏該位置參數的情況。

critical_argument01=$variable1_

# The extra character can be stripped off later, like so.
# 這個多餘的字元可以在稍候被去掉,例如這樣子…。
variable1=${variable1_/_/}
# Side effects only if $variable1_ begins with an underscore.
# 僅在 $variable1_ 以一個底線為開頭時有副作用。
# This uses one of the parameter substitution templates discussed later.
# 這個用到了 "參數替換" 的其中一種範本,這點之後會再詳細描述。
# (Leaving out the replacement pattern results in a deletion.)
# (若沒有填替代用的 pattern (第二個斜線後保持為空),則效果等同刪除。)

#  A more straightforward way of dealing with this is
#  處理這種事有一個更直觀的方式:
#+ to simply test whether expected positional parameters have been passed.
#+ 測試預期的位置參數是否有傳值進來。
if [ -z $1 ]
then
  exit $E_MISSING_POS_PARAM
fi

#  However, as Fabian Kreutz points out,
#  不過,如同 Fabian Kreutz 所指出,
#+ the above method may have unexpected side-effects.
#+ 上述方式可能會有意想不到的負作用。
#  A better method is parameter substitution:
#  比較好的方式是使用 "參數替換":
#         ${1:-$DefaultVal}
#  See the "Parameter Substition" section
#+ in the "Variables Revisited" chapter.
#  詳情請參閱:第十章 "操弄變數" 的第二節 "參數替換"。

Example 4-6. wh, whois domain name lookup

範例 4-6. *wh*, *whois* 網域名稱查尋

#!/bin/bash
# ex18.sh

# Does a 'whois domain-name' lookup on any of 3 alternate servers:
# 於下列三個伺服器選一個做有如 'whois 網域名稱' 的網域名稱查尋功能:
#                    ripe.net, cw.net, radb.net

# Place this script -- renamed 'wh' -- in /usr/local/bin
# 請將此腳本命名成 'wh' 並且置放於 /usr/local/bin 目錄底下。

# Requires symbolic links:
# 需要額外建立下列這些的符號連結:
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
# ln -s /usr/local/bin/wh /usr/local/bin/wh-apnic
# ln -s /usr/local/bin/wh /usr/local/bin/wh-tucows

E_NOARGS=75


if [ -z "$1" ]
then
echo "Usage: `basename $0` [domain-name]"
  exit $E_NOARGS
fi

# Check script name and call proper server.
# 檢查腳本名稱跟所呼叫的伺服器名稱之正確性。
case `basename $0` in    # Or:    case ${0##*/} in
    "wh"       ) whois $1@whois.tucows.com;;
    "wh-ripe"  ) whois $1@whois.ripe.net;;
    "wh-apnic" ) whois $1@whois.apnic.net;;
    "wh-cw"    ) whois $1@whois.cw.net;;
    *          ) echo "Usage: `basename $0` [domain-name]";;
esac

exit $?

The shift command reassigns the positional parameters, in effect shifting them to the left one notch.

"shift" 指令會將所有的位置變數往左邊位移一格以重新賦值所有位置變數的值。

$1 <--- $2, $2 <--- $3, $3 <--- $4, etc.

$1 <--- $2, $2 <--- $3, $3 <--- $4, 等等,以此類推。

The old $1 disappears, but $0 (the script name) does not change.

原本舊的 $1 會消失不見,但 $0 (腳本名稱) 不會被動到。

If you use a large number of positional parameters to a script, shift lets you access those past 10, although {bracket} notation also permits this.

若你在某個腳本內使用了大量的位置參數,則 "shift" 指令讓你可以存取超過 10 個以上的位置變數而不需用到 {括號標記}。

Example 4-7. Using shift

範例 4-7. 使用 shift 指令

#!/bin/bash
# shft.sh: Using 'shift' to step through all the positional parameters.
# shft.sh: 使用 'shift' 指令來逐步穿過所有的位置參數。

#  Name this script something like shft.sh,
#  為此腳本取個名子,像是 shft.sh,
#+ and invoke it with some parameters.
#+ 並且帶些參數呼叫它。
#+ For example:
#+ 例如:
#             sh shft.sh a b c def 83 barndoor

until [ -z "$1" ]  # Until all parameters used up . . .
                   # 直到用光所有參數 . . .
do
  echo -n "$1 "
  shift
done

echo               # Extra linefeed.
                   # 多換一行。

# But, what happens to the "used-up" parameters?
# 但是,如果 "用光" 參數會發生什麼事?
echo "$2"
#  Nothing echoes!
#  什麼也沒印!
#  When $2 shifts into $1 (and there is no $3 to shift into $2)
#  當 $2 移進 $1 (且這裡並沒有 $3 可以移進 $2)
#+ then $2 remains empty.
#+ 則 $2 保持為空。
#  So, it is not a parameter *copy*, but a *move*.
#  因此,它不是 "複製" 參數,而是 "移動"。

exit

#  See also the echo-params.sh script for a "shiftless"
#  另外可以參考:範例 15-13. "echo-params.sh" 腳本裡
#+ alternative method of stepping through the positional params.
#+ 有另一種 "shiftless" (不使用 "shift" 指令) 的方式,一樣可以逐步穿過所有的位置參數。

The shift command can take a numerical parameter indicating how many positions to shift.

"shift" 指令還可以帶一個數字參數,以表明指定要位移多少格。

#!/bin/bash
# shift-past.sh

shift 3    # Shift 3 positions.
           # 位移 3 格。
#  n=3; shift $n
#  Has the same effect.
#  有同樣效果。

echo "$1"

exit 0

# ======================== #
$ sh shift-past.sh 1 2 3 4 5
4

#  However, as Eleni Fragkiadaki, points out,
#  不過,如同 Eleni Fragkiadaki 所指出,
#+ attempting a 'shift' past the number of
#+ 在企圖 'shift' 超過…
#+ positional parameters ($#) returns an exit status of 1,
#+ 帶入的引數個數時,將會回傳 1 的退出狀態碼,
#+ and the positional parameters themselves do not change.
#+ 並且留下原本所有的位置參數內容沒變。
#  This means possibly getting stuck in an endless loop. . . .
#  這表示可能會陷入無限迴圈…
#  For example:
#  例如:
#      until [ -z "$1" ]
#      do
#         echo -n "$1 "
#         shift 20    #  If less than 20 pos params,
#      done           #+ then loop never ends!
#                     #  若輸入低於 20 個位置參數,則會陷入無限迴圈!
#
# When in doubt, add a sanity check. . . .
# 若懷疑,則可以加入下列檢查…
#           shift 20 || break
#                    ^^^^^^^^

The shift command works in a similar fashion on parameters passed to a function.

對於傳入函式 (function) 裡的引數,一樣可以用 "shift" 指令以同樣類似的方式來操作。

See Example 36-18.

`詳情請參閱:範例 36-18。