2017-01-09 156 views
0

我有這個shell腳本,我想修改它以確保它在Bash環境中正確運行。從shell腳本可移植地設置環境變量

這裏的腳本:

#!/bin/zsh 

# Some constants. The first two will become env variables. 
UPDATE_DNS_API_HOST="https://example.com" 
UPDATE_DNS_API_URL="$UPDATE_DNS_API_HOST/my_end_point" 
CURRENT_PUBLIC_IP=$(curl -s "$UPDATE_DNS_API_URL" | grep -o '".*"' | tr -d '"') 

if [[ $PUBLIC_IP == $CURRENT_PUBLIC_IP ]]; then 
    echo "Current IP: "$CURRENT_PUBLIC_IP" already set." 
else 
    response=$(curl -s -H "Content-Type: application/json" \ 
      --data "$CURRENT_PUBLIC_IP" "$UPDATE_DNS_API_URL") 
    echo $response 
    export PUBLIC_IP="$CURRENT_PUBLIC_IP" 
fi 

這裏是我的問題:

  • 我應該改變的第一行#!/bin/bash
  • 目前還不清楚,當變量需要報價,當他們不這樣做,特別是在條件語句中。你能指點我一些資源嗎?
  • 我已經看到有關單支架與雙支架的條件變化。我應該使用哪一個?
  • 運行腳本後,$ PUBLIC_IP似乎沒有設置。有什麼不同的方式我應該設置env變量?

歡迎任何其他反饋意見。

+0

如果你真的需要兼容,那麼你應該使用'#!/ bin/sh',並將'[[$ PUBLIC_IP == $ CURRENT_PUBLIC_IP]]'改爲'[「$ PUBLIC_IP」=「 $ CURRENT_PUBLIC_IP「]'。 ('=='是bash和zsh中的一個擴展,但是POSIX標準指定的唯一字符串比較操作符是一個'=')。 –

+2

順便說一下,你自己的變量的大寫名字實際上是不好的形式。環境變量的POSIX規範指定對操作系統或外殼具有含義的名稱將具有全大寫名稱,並且爲至少一個小寫字符保留名稱以供應用程序使用。這意味着'PUBLIC_IP'對於將來的bash版本(改變'/ dev/tcp'綁定的位置?)可能是有意義的,而保證名爲'public_ip'的變量不會跺腳。 –

+0

...查看http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html re:上述引用的命名約定。 –

回答

1

一個相關的事情要記住這裏是UNIX進程可以修改環境變量爲自己和未來的孩子,他們開始 - 他們的父母,而沒有父進程的直接參與。


如果你的目的是在封閉shell中設置一個變量,一個相當常見的方法是在stdout上發出shell命令。這意味着任何不是的shell命令都應該移到stderr(無論如何,這是適當的做法,因爲stderr被指定爲適用於信息文本和狀態內容)。

這個版本確實需要的bash,而不是/bin/sh,但使用printf '%q',以確保它能夠在一個eval -safe的方式,所有的ksh衍生物(KSH時,bash,zsh中)應該能夠讀取產生的變量名。

#!/bin/bash 
# note that while this runs with bash, ksh and zsh will also be able to eval its output 
# ...POSIX sh too, when there aren't nonprintable characters causing $''-style quoting 
# ...to be used. 

# usage: emit_cmd varname ... 
# 
# emit code that defines a variable when evaluated on stdout 
emit_cmd() { 
    for varname; do 
    printf 'export %q=%q; ' "$varname" "${!varname}" 
    done 
} 

# Some constants. The first two will become env variables. 
UPDATE_DNS_API_HOST="https://example.com" 
UPDATE_DNS_API_URL="$UPDATE_DNS_API_HOST/my_end_point" 

# print definitions of those variables to stdout 
emit_cmd UPDATE_DNS_API_HOST UPDATE_DNS_API_URL 

CURRENT_PUBLIC_IP=$(curl -s "$UPDATE_DNS_API_URL" | grep -o '".*"' | tr -d '"') 

if [[ $PUBLIC_IP = $CURRENT_PUBLIC_IP ]]; then 
    echo "Current IP: $CURRENT_PUBLIC_IP already set." >&2 
else 
    response=$(curl -s -H "Content-Type: application/json" \ 
      --data "$CURRENT_PUBLIC_IP" "$UPDATE_DNS_API_URL") 
    echo "$response" >&2 
    PUBLIC_IP="$CURRENT_PUBLIC_IP" emit_cmd PUBLIC_IP 
fi 

如果這個腳本的名稱ip-lookup下保存的,它定義的變量可以導入到當前的外殼採用:

eval "$(ip-lookup)" 

使用這個約定保持與現有的Unix工具,如ssh-agent的相容性需要修改環境變量。


注意,我讓現有的公約相對於變量名,但如果你有機會,你應該切換到小寫的名字遵守relevant POSIX convention

1
  • 如果你希望有人誰調用script.shbash,而不是zsh下運行它,那麼你必須修復的家當。

  • 在內部對雙引號:

    echo "Current IP: "$CURRENT_PUBLIC_IP" already set." 
    

    是非正統的,而不是一個好主意。用途:

    echo "Current IP: $CURRENT_PUBLIC_IP already set." 
    

    你大概是想雙引號:

    echo "$response" 
    

    否則,我覺得你很確定。至於資源,您可以查看Bash manual,或使用shellcheck.com等設施,或諮詢Bash FAQ

  • 如果你使用[[ … ]],你可能會寫得很好。 shell語法的正常規則在[[ … ]]中被暫停,並且將學習Bourne shell的人搞糊塗了,並且不會爲了學習Bash的那部分而煩惱。

  • 您必須點(. script.sh)或來源(source script.shexport PUBLIC_IP="$CURRENT_PUBLIC_IP"的腳本對調用shell有任何影響。否則,它會設置運行腳本的shell的環境,但不會影響調用shell。

    如果您確定要使用帶有dot命令的腳本,那麼您應該考慮它設置的變量數量應該在其完成之前取消設置。也許在它開始之前,它們中的任何一個是否已設置好。使用聲明爲本地的所有變量創建一個函數,除了要導出的變量之外,這會使生活更輕鬆。當腳本作爲單獨的進程運行時,您不必擔心這一點。

相關問題