2015-11-19 49 views
18

對於我工作的個人開發和項目,我們使用四個空格而不是製表符。 但是,我需要使用heredoc,如果不打破縮進流程,我無法做到這一點。使用空格縮進heredocs

唯一的工作方式做到這一點,我能想到的會是這樣:

usage() { 
    cat << ' EOF' | sed -e 's/^ //'; 
    Hello, this is a cool program. 
    This should get unindented. 
    This code should stay indented: 
     something() { 
      echo It works, yo!; 
     } 
    That's all. 
    EOF 
} 

有沒有更好的方式來做到這一點?

請讓我知道這是否屬於Unix/Linux Stack Exchange

+1

不,這是一個編程問題,它是合法的在這裏。感謝您的檢查。 –

+0

這似乎是一個很好,清晰,直接的方法。不知道別的方法。當我得到更多選票時,我會贊成它,也許有人知道一些有趣的事情。 –

+0

噢,很好的解決方案!不幸的是在大多數其他語言中不可能,使縮進的代碼塊變得棘手。 – Kenney

回答

21

(如果使用的是bash 4,滾動到底爲了什麼,我認爲是純粹的外殼和可讀性的最佳組合。)

對於shell腳本,使用標籤是不喜好或風格的問題;這就是語言的定義。

usage() { 
    # Lines between EOF are each indented with the same number of tabs 
    # Spaces can follow the tabs for in-document indentation 
    cat <<-EOF 
     Hello, this is a cool program. 
     This should get unindented. 
     This code should stay indented: 
      something() { 
       echo It works, yo!; 
      } 
     That's all. 
    EOF 
} 

另一種選擇是避免在此文件完全在不必使用更多的報價和線路延續的費用:

usage() { 
    printf '%s\n' \ 
     "Hello, this is a cool program." \ 
     "This should get unindented." \ 
     "This code should stay indented:" \ 
     " something() {" \ 
     "  echo It works, yo!" \ 
     " }" \ 
     "That's all." 
} 

如果你願意放棄POSIX兼容,可以使用陣列,以避免顯式線延續:

usage() { 
    message=(
     "Hello, this is a cool program." 
     "This should get unindented." 
     "This code should stay indented:" 
     " something() {" 
     "  echo It works, yo!" 
     " }" 
     "That's all." 
    ) 
    printf '%s\n' "${message[@]}" 
} 

下面以這裏DOCUME nt再次,但這次用bash 4的readarray命令填充數組。參數展開負責從每個謊言的開頭刪除固定數量的空格。

usage() { 
    # No tabs necessary! 
    readarray message <<' EOF' 
     Hello, this is a cool program. 
     This should get unindented. 
     This code should stay indented: 
      something() { 
       echo It works, yo!; 
      } 
     That's all. 
    EOF 
    # Each line is indented an extra 8 spaces, so strip them 
    printf '%s' "${message[@]#  }" 
} 

最後一種變化:您可以使用擴展模式來簡化參數擴展。無需統計有多少空格用於縮進,只需使用選定的非空格字符結束縮進,然後匹配固定前綴即可。我使用:。 ( 後面的空格代表可讀性;可以通過對前綴模式的輕微更改而刪除)

(另外,作爲一個缺點,您使用here-doc分隔符以空格開頭的是它阻止你在here-doc中執行擴展,如果你想這樣做,你必須保持分隔符不縮進,或者對你的no-tab規則做一個小例外,並使用<<-EOF和一個製表縮進結束分隔符。)

usage() { 
    # No tabs necessary! 
    closing="That's all" 
    readarray message <<EOF 
     : Hello, this is a cool program. 
     : This should get unindented. 
     : This code should stay indented: 
     :  something() { 
     :   echo It works, yo!; 
     :  } 
     : $closing 
EOF 
    shopt -s extglob 
    printf '%s' "${message[@]#+(): }" 
    shopt -u extglob 
} 
0
geta() { 
    local _ref=$1 
    local -a _lines 
    local _i 
    local _leading_whitespace 
    local _len 

    IFS=$'\n' read -rd '' -a _lines ||: 
    _leading_whitespace=${_lines[0]%%[^[:space:]]*} 
    _len=${#_leading_whitespace} 
    for _i in "${!_lines[@]}"; do 
    printf -v "$_ref"[$_i] '%s' "${_lines[$_i]:$_len}" 
    done 
} 

gets() { 
    local _ref=$1 
    local -a _result 
    local IFS 

    geta _result 
    IFS=$'\n' 
    printf -v "$_ref" '%s' "${_result[*]}" 
} 

這是一個稍微不同的方法whic由於printf被分配給數組元素,h需要Bash 4.1。 (對於以前的版本,請替換下面的geta函數)。它處理任意領先的空白,而不僅僅是預定的數量。

第一功能,geta,從stdin,條前導空白讀取並返回結果的名稱在獲得通過在數組中。

第二,gets,做同樣的事情作爲geta但返回單個串換行符保持不變(除了最後一行)。

如果您將現有變量的名稱傳遞給geta,請確保它已爲空。

調用geta像這樣:

$ geta hello <<'EOS' 
> hello 
> there 
>EOS 
$ declare -p hello 
declare -a hello='([0]="hello" [1]="there")' 

gets

$ unset -v hello 
$ gets hello <<'EOS' 
>  hello 
>  there 
> EOS 
$ declare -p hello 
declare -- hello="hello 
there" 

這種做法應該適用的前導空格字符的任意組合,只要它們是所​​有後續行相同的字符。該功能根據第一行中前導空白字符的數量,從每行的前面剝離相同數量的字符。

所有變量都以下劃線開頭的原因是爲了儘量減少名稱與傳遞數組名稱衝突的機會。你可能想重寫這個文件來給它們加前綴,使它們不太可能相互碰撞。

要OP的功能使用:

gets usage_message <<'EOS' 
    Hello, this is a cool program. 
    This should get unindented. 
    This code should stay indented: 
     something() { 
      echo It works, yo!; 
     } 
    That's all. 
EOS 

usage() { 
    printf '%s\n' "$usage_message" 
} 

如前所述,對於猛砸比4.1以上:

geta() { 
    local _ref=$1 
    local -a _lines 
    local _i 
    local _leading_whitespace 
    local _len 

    IFS=$'\n' read -rd '' -a _lines ||: 
    _leading_whitespace=${_lines[0]%%[^[:space:]]*} 
    _len=${#_leading_whitespace} 
    for _i in "${!_lines[@]}"; do 
    eval "$(printf '%s+=("%s")' "$_ref" "${_lines[$_i]:$_len}")" 
    done 
}