2015-11-19 36 views
18

Çalıştığım kişisel gelişim ve projeler için sekmeler yerine dört boşluk kullanırız. Bununla birlikte, heredoc'u kullanmam gerekiyor, ve bunu donduru akışını bozmadan yapamam.Heredogları boşluklarla girme

Aklıma Bunu yapmanın tek bir çalışma yolu bu olacaktır: Bunu yapmak için daha iyi bir yolu

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 
} 

var mı?

Bunun yerine Unix/Linux Stack Exchange'a ait olup olmadığını bildirin.

+1

Hayır, burada okunaklı bir programlama soru. Kontrol ettiğin için teşekkürler. –

+0

Bu, bunu yapmanın iyi, açık ve anlaşılır bir yolu gibi görünüyor. Başka yollar bilmiyorum. Daha fazla oy aldığımda bunu söyleyeceğim, belki birisi ilginç bir şey biliyordur. –

+0

Ow, hoş bir çözüm! Maalesef diğer birçok dilde mümkün değil, kodların girintilemesini zorlaştırıyor. – Kenney

cevap

21

(eğer bash 4 kullanıyorsanız, ben saf kabuk ve okunabilirliği en iyi kombinasyonu olduğunu düşünüyorum ne için sonuna kadar ilerleyin.) Kabuk komut dosyaları için

, sekmeleri kullanarak tercihi veya stil meselesi değildir; dil nasıl tanımlanır.

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 
} 

Diğer bir seçenek tırnak ve hat continuations daha kullanmak zorunda pahasına, tamamen burada belgeyi kaçınmaktır: POSIX uyumluluğu vazgeçmek isteyen varsa

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." 
} 

, bir kullanabilirsiniz aşağıdaki bir buraya DOCUME kullanır

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[@]}" 
} 

: dizi açık hat devamlılık önlemek için nt tekrar, ancak bu sefer bir dizi doldurmak için komutuyla. Parametre genişletme, her bir yalanın başlangıcından sabit sayıda boşluğun kaldırılmasına özen gösterir.

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[@]#  }" 
} 

Son bir değişiklik: parametre genişletmeyi basitleştirmek için genişletilmiş bir desen kullanabilirsiniz. Girinti için kaç alanın kullanıldığını hesaba katmak yerine, sadece boşluk olmayan bir karakterle girintiyi sonlandırın, ardından sabit önekle eşleştirin. : kullanıyorum. ( 'u izleyen boşluk, okunabilirlik içindir; önek şablonuna küçük bir değişiklikle bırakılabilir.)

(Ayrıca, bir belgeyi kullanarak, burada bir belgeyi kullanmanın çok güzel bir hilesidir. Bu boşluk ile başlıyor, buradaki dokümanın içinde genişletme yapmanıza engel oluyor olmasıdır.Bunu yapmak isterseniz, sınırlayıcıyı geçersiz kılmanız ya da sekme kuralınız için küçük bir istisna yapmanız ve <<-EOF'u kullanmanız gerekir. ve sekme girintili kapatma ayraç.) Bu, biraz daha farklı bir yaklaşım

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[*]}" 
} 

yüklenebileceğini h printf'in dizi elemanlarına atanması nedeniyle Bash 4.1'i gerektirir. (önceki sürümler için, aşağıdaki geta işlevini kullanın). Sadece önceden belirlenmiş bir miktar değil, keyfi lider boşlukla ilgilenir.

ilk fonksiyon, geta, Stdin, şeritler gelen boşluk okur ve adı geçirildi dizide bir sonuç verir.

ikinci gets, geta aynı işlevi ancak tek bir karakter dizisi döndürür yeni hatlar bozulmadan (sonuncu hariç).

Varolan değişkenin adını geta'a iletirseniz, zaten boş olduğundan emin olun. şöyle

çağırır 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" 

Bu yaklaşım o kadar uzun süre sonraki tüm hatlar için aynı karakterler gibi, önde gelen herhangi bir boşluk karakteri kombinasyonu için çalışmalıdır. Fonksiyon, ilk satırdaki önde gelen boşluk karakterlerinin sayısına bağlı olarak, her satırın önünden aynı sayıda karakter çıkarır.

Tüm değişkenlerin alt çizgi ile başlaması nedeni, geçirilen dizi adıyla ad çarpışma olasılığını en aza indirmektir. Bunu, çarpışması daha da az olası bir şeyle öneklemek için yeniden yazmak isteyebilirsiniz.

OP'ın işlevinde kullanmak için: Belirtildiği gibi

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 daha eski Bash için:

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 
}