Bash'deki bir değişkene bir yorumlu değer nasıl atanır?


374

Bu çok satır dize (tırnak dahil) var:

abc'asdf"
$(dont-execute-this)
foo"bar"''

Bash'ta bir yorumlu metin kullanarak bir değişkene nasıl atayabilirim?

Yeni satırları korumam gerekiyor.

Dizedeki karakterlerden kaçmak istemiyorum, bu sinir bozucu olurdu ...


@JohnM - Ben sadece CDR komut 'EOF'ile kaçan satır satırları ile tek tırnaklı bir yorumlu atama denedim ` in the content: if the second line has , ben geri alıyorum: " .sh: line X: cd: komut bulunamadı "; ama çift tırnak koyarsam "EOF"; bash değişkenleri ${A}dize olarak korunmaz (genişler); ama sonra, hat sonları vardır korunmuş - Ben bir komutu çalıştırarak bir sorun yok ve cdikinci satırda ( ve hem 'EOF' ve "EOF" iyi de oynamak gibi evalsaklanan komutlar kümesi çalıştırmak için, dize değişkeni ). Şerefe!
sdaau

1
... ve önceki yorumuma eklemek için: çift yönlü "EOF"değişkendeki bash yorumları "#" , çağrılırsa eval $VAR, komut dosyasının geri kalanının yorumlanmasına neden olur, çünkü burada $ VAR tek bir satır olarak görülecektir ; bash #yorumlarını çok satırlı komut dosyasında kullanabilmek için çift eval call: tırnaklı "eval" $ VAR değişkeninde de değişkendir .
sdaau

@sdaau: evalBu yöntemlerle ilgili sorunlar yaşadım , ancak evalyapılandırma dosyasında tanımlanan bazı değişkenler olan bazı paketlerin bir parçası olduğu için izlemedim . Hata mesajı vardı: /usr/lib/network/network: eval: line 153: syntax error: unexpected end of file. Yeni bir çözüme geçtim.
Golar Ramblar

Yanıtlar:


515

catBununla eşleşmeyen teklifleri işe yaramaz şekilde kullanmaktan kaçınabilirsiniz :

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

Değişkeni yankıladığınızda alıntılamazsanız, yeni satırlar kaybolur. Alıntı yapmak onları korur:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Kaynak koddaki okunabilirlik için girintiyi kullanmak istiyorsanız, daha az tondan sonra bir tire kullanın. Girinti sadece sekmeler kullanılarak yapılmalıdır (boşluk bırakılmamalıdır).

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Bunun yerine, sonuçtaki değişkenin içeriğindeki sekmeleri korumak istiyorsanız, sekmeyi kaldırmanız gerekir IFS. Buradaki doc ( EOF) için terminal işareti girintili olmamalıdır.

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

Tab basarak komut satırında sokulabilir Ctrl- V Tab. Bir düzenleyici kullanıyorsanız, hangisine bağlı olarak, bu da işe yarayabilir veya sekmeleri otomatik olarak boşluklara dönüştüren özelliği kapatmanız gerekebilir.


117
Eğer betiğinizde set -o errexit(aka set -e) varsa ve bunu kullanırsanız, betiğinizi sonlandırır çünkü readEOF'a ulaştığında sıfır olmayan bir dönüş kodu döndürür.
Mark Byers

14
@MarkByers: Bu asla kullanmamamın set -eve her zaman kullanılmamasını önermemin nedenlerinden biri . Bunun yerine uygun hata işlemeyi kullanmak daha iyidir. trapsenin arkadaşın. Diğer arkadaşlar: elseve ||diğerleri arasında.
sonraki duyuruya kadar duraklatıldı.

7
catBöyle bir durumda kaçınmaya gerçekten değer mi? Bir değişkenle bir yorumlu metin atamak catiyi bilinen bir deyimdir. Her nasılsa readküçük faydalar imho şeyler şeyler gizler.
Gregory Pakosz

6
@ulidtko Bunun nedeni dile boş dize arasında boşluk olmaması ; bashçökmeler -rd''basitçe için -rdönce readbu kadar, onun argümanları gördüğü VARiçin argüman olarak kabul edilir -d.
chepner

6
Bu biçimde, readsıfırdan farklı bir çıkış kodu ile döner. Bu, hata denetimi etkinleştirilmiş bir komut dosyasında (ör. set -e) Bu yöntemi ideal olandan daha az yapar .
İsviçre

245

catDeğişkeninize çıktısını şu şekilde atamak için $ () kullanın :

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

END_HEREDOC başlangıcını tek tırnakla sınırlandırdığınızdan emin olun.

Sonlandırma sınırlayıcı sınırlayıcısının END_HEREDOCsatırda yalnız olması gerektiğini unutmayın (bu nedenle sonlandırma parantezi sonraki satırdadır ).

@ephemientCevabınız için teşekkürler .


1
Aslında, bu bazı durumlarda alıntılar yapmanıza izin verir. Bunu Perl'de kolayca yapmayı başardım ...
Neil

32
+1. Bu, en azından gözlerim için en okunabilir çözüm. Değişkenin adını, okuma komutuna gömmek yerine sayfanın en solunda bırakır.
Clayton Stanley

12
PSA: satırsonlarını korumak için değişkenin tırnak içine alınması gerektiğini unutmayın . echo "$VAR"yerine echo $VAR.
sevko

7
Bu hoş ashve OpenWRT nerede readdesteklemiyor -d.
David Ehrmann

3
Sebebi çekemediğim nedenlerden dolayı, bir yorumda eşleştirilmemiş bir geri tepme varsa, "beklenmedik bir EOF" hatasıyla başarısız olur .
Radon Rosborough

79

Bu Dennis yönteminin bir çeşididir, senaryolarda daha zarif görünüyor.

fonksiyon tanımı:

define(){ IFS='\n' read -r -d '' ${1} || true; }

kullanımı:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

zevk almak

ps desteklemeyen kabuklar için bir 'okuma döngüsü' sürümü yaptı read -d. çalışmalı set -euve eşleştirilmemiş backticks ile çalışmalı , ancak çok iyi test edilmemelidir:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }

1
Bu sadece yüzeysel olarak işe yarıyor gibi görünüyor. Define işlevi 1 durumunu döndürür ve neyin düzeltilmesi gerektiğinden emin değilim.
fny

1
Bu da kabul edilen cevaptan üstündür, çünkü bash'a ek olarak POSIX sh'yi desteklemek için değiştirilebilir ( readfonksiyondaki bir döngü, -d ''yeni satırları korumak için gerekli bashizmden kaçınmak için ).
ELLIOTTCABLE

Alt kabuktaki kedi seçeneğinden farklı olarak, bu yorumlu metinte eşleştirilmemiş backticks ile çalışır . Teşekkür ederim!
Radon Rosborough

Bu çözüm set -eset ile çalışır , ancak seçilen cevap çalışmaz. http://unix.stackexchange.com/a/265151/20650
Nedeniyle

2
@fny ps dönüş durumu uzun zamandır düzeltildi
ttt

36
VAR=<<END
abc
END

çalışmıyor çünkü stdin'i umursamayan bir şeye, yani atamaya yönlendiriyorsunuz

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

çalışıyor, ancak orada bunu kullanmanızı engelleyebilecek bir arka tic var. Ayrıca, backticks kullanmaktan kaçınmalısınız, komut değiştirme gösterimini kullanmak daha iyidir $(..).

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A

Sorumu $ (yürütülebilir) içerecek şekilde güncelledim. Ayrıca, yeni satırları nasıl korursunuz?
Neil

2
@ l0st3d: Çok yakın ... $(cat <<'END'Bunun yerine kullanın . @Neil: Son satırsonu değişkenin bir parçası olmayacak, gerisi korunacak.
ephemient

1
Yeni satırlar korunmuş gibi görünmüyor. Yukarıdaki örneği çalıştırıyorum: "sdfsdf sdfsdf sdfsfds" ... ah! Ama yazma echo "$A"(yani A çift tırnak içine koyarak) ve satırsonu görüyorsun!
Darren Cook

1
@Darren: aha! Newlines sorununu fark etmiştim ve çıktı değişkeninin etrafındaki tırnak işaretlerini kullanmak sorunu çözüyor. Teşekkür!
javadba

1
İlginçtir, zira ilk örnek cilvesi, bir tutam böyle eğreti açıklama bloğu için kullanabilirsiniz: REM=<< 'REM' ... comment block goes here ... REM. Veya daha kompakt : << 'REM' .... "REM", "NOTLAR" veya "SCRATCHPAD" vb. Gibi bir şey olabilir.
Beejor

34

Hala yeni satırları koruyan bir çözüm yok.

Bu doğru değil - muhtemelen sadece yankı davranışı tarafından yanlış yönlendiriliyorsunuz:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines


5
Gerçekten bu, bir değişkenin alıntısının nasıl çalıştığıdır. Tırnaklar olmadan, bunları farklı parametreler olarak ekler, alan sınırlandırılır, tırnaklarla tüm değişken içerikleri tek bir argüman olarak ele alınır
Czipperz

11

Neil'in cevabını dallamak , genellikle bir değişkene ihtiyacınız yoktur, bir işlevi bir değişkenle aynı şekilde kullanabilirsiniz ve okumak satır içi veya readtabanlı çözümlerden çok daha kolaydır .

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''

9

Dizi bir değişkendir, bu durumda mapfile çalışır

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

Sonra böyle yazdırabilirsiniz

printf %s "${y[@]}"

3

bir değişkene yorumlu değer atama

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

bir komutun argümanı olarak kullanılır

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"

İlk yöntemi denediğimde, hatlar arasında hat sonlandırıcı yok gibi görünüyor. Linux makinemde bir tür yapılandırma olmalı mı?
Kemin Zhou

Bu muhtemelen değişkeninizi yankıladığınız zaman, onun içine tırnak echo "$VAR"
Brad Parks

1

Kendimi içinde NULL ile bir dize okumak zorunda buldum, bu yüzden burada her şeyi okuyacak bir çözüm . Aslında NULL ile uğraşıyorsanız, onaltılık seviyede bununla uğraşmanız gerekecektir.

$ cat> read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

Kanıt:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

HEREDOC örneği (^ J, ^ M, ^ I ile):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE

0

Dimo414'ün cevabı için teşekkürler , kolayca yanı metinde tırnak ve değişkenleri sahip olabileceği bu onun harika bir çözüm nasıl çalıştığını gösterir ve gösteriler:

örnek çıktı

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main

Bunu nasıl ele aldığını görmek için metinde eşsiz bir alıntı görmek ilginç olurdu. Belki 'çıldırmayın, "$ COUNT" dosya var`, bu yüzden kesme işareti / tek alıntı ilginç şeyler yapabilir.
dragon788

-10
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.