Girintili bir değişkene birden çok satır üzerinden bir dize değeri atamak nasıl?


54

Sorun:

  1. Bir değişkene terbiyeli uzun bir değer atamam gerekiyor.
  2. Komut dizimin tüm satırları belirli sayıda sütunun altında olmalıdır.

Bu yüzden birden fazla satır kullanarak atamaya çalışıyorum.

Girintiler olmadan yapmak kolaydır:

VAR="This displays without \
any issues."
echo "${VAR}"

Sonuç:

This displays without any issues.

Ancak girintilerle:

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"

Sonuç:

This displays with      extra spaces.

Bu alanlar olmadan nasıl zarif bir şekilde atayabilirim?

Yanıtlar:


30

Burada sorun, değişkeni çift tırnaklı ("") çevreleyen olmasıdır. Kaldır ve her şey yoluna girecek.

    VAR="This displays with \
    extra spaces."
    echo ${VAR}

Çıktı

 This displays with extra spaces.

Burada sorun, bir değişkenin çift tırnak içine alınmasının tüm beyaz boşluk karakterlerini koruduğudur. Açıkça ihtiyacınız olursa, bu kullanılabilir.

Örneğin,

$ echo "Hello     World    ........ ...            ...."

basacak

Hello     World    ........ ...            ....

Ve teklifleri kaldırırken, farklı

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....

Burada Bash, metindeki ekstra boşlukları kaldırır, çünkü ilk durumda metnin tamamı "tek" bir argüman olarak alınır ve böylece ekstra boşluklar korunur. Ancak ikinci durumda echokomut metni 5 argüman olarak alır.

Bir değişkeni alıntılamak, argümanları komutlara iletirken de yardımcı olacaktır.

Aşağıdaki komutta, echoyalnızca tek bir argüman alır."Hello World"

$ variable="Hello World"
$ echo "$variable"

Ama aşağıda senaryo durumunda echoolduğu gibi iki argüman alır HelloveWorld

$ variable="Hello World"
$ echo $variable

Çoğu cevap iyi çalıştı, ama bu en basit olanıydı. Teşekkürler!
Sman865

12
@ Sman865 - lütfen bunun bana neredeyse kesin olarak sunulan en karmaşık cevap olduğunu söylerken bana inanın ve aynı zamanda çoğu bakımdan yanlıştır - özellikle de açılış bildirgesinde. Değer tahsisini çevreleyen herhangi bir sorun, daha sonraki genişlemesiyle hiçbir şekilde ilişkili olamaz - bu geriye doğru. Üzgünüm Kannan, ama bu cevap hem yanlış hem de yanlış. Alan bölme açılımları $IFSgüçlü ve evrensel bir araçtır - ancak bu şekilde yazılmış kod hiçbir zaman güvenilir sonuçlar veremez.
mikeserv

2
Bir değişkeni genişletirken tırnak kullanmamak, dosya adı genişletme (herhangi biri * ? []) ile başlayan er ya da geç sorunlara neden olur .
mr.spuratic

Bash genişlemesini önlemek için değişkenlerin çift tırnak içine alınması gerektiğine katılıyorum. Ancak özel durumlar için kodun karmaşıklaşmasını önlemekten kaçınabiliriz.
Kannan Mohan

1
@Mikeserv ile aynı fikirdeyim, yüz değerinden alındığında bu çok kötü bir tavsiye. Bu sonuçsuz kullanılırsa bozulur. Örneğin, değişken bir komuta argüman olarak iletilirse, her kelimenin ayrı bir argüman olarak bölünmesini istemezsiniz.
haridsv

28

Esuoxu ve Mickaël Bucas tarafından verilen çözümler , bunu yapmanın en yaygın ve taşınabilir yoludur.

İşte size birkaç bashçözüm (bazıları aynı zamanda diğer kabuklarda da çalışması gerekir zsh). Öncelikle +=append operatörüyle (bir tamsayı değişkeni, normal bir değişken ve bir dizi için biraz farklı bir şekilde çalışır).

text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
text+="tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
text+="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." 

Metinde yeni satırlar (veya diğer boşluklar / çıkışlar) istiyorsanız, $''bunun yerine alıntıyı kullanın:

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'

Sonra, printf -vbir değişkene formatlanmış bir değer atamak için

printf -v text "%s" "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
                    "do eiusmod empor incididunt ut labore et dolore magna aliqua. "\
                    "Ut enim ad minim veniam ..."

Buradaki hile, format tanımlayıcılardan daha fazla argüman olduğu, bu nedenle çoğu printffonksiyonun aksine bash, bitene kadar format dizesini yeniden kullanır. \nBiçim dizgesinin içine bir sayı koyabilir veya boşluk ile baş etmek için $ '' ((veya her ikisini)) kullanabilirsiniz.

Sonra, bir dizi kullanarak:

text=("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
      "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." )

Ayrıca +=, metni satır satır satır oluşturmak için de kullanabilirsiniz (not edin ()):

text+=("post script")

Bununla birlikte, tüm metin içeriğini bir seferde istiyorsanız, diziyi "düzleştirmeyi" unutmayın

echo "$text"      # only outputs index [0], the first line
echo "${text[*]}" # output complete text (joined by first character of IFS)

(tamsayı indeksli diziler, ilişkisel dizilerin aksine, örtük olarak sıralanır) Bu, çizgileri manipüle edebileceğiniz ve gerektiğinde dilimleyebileceğiniz ve zar alabileceğinizden size biraz daha fazla esneklik sağlar .

Son olarak, readveya veya readarray"burada-belge" kullanarak:

read -r -d '' text <<-"EOT"
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ...
EOT

readarray -t textarray <<-"EOT"
        Lorem [...]
EOT  

Buradaki belge formu, <<-tüm önde gelen sert sekmelerin girişten kaldırıldığı anlamına gelir; bu nedenle metninizi girintili yapmak için sekmeleri kullanmanız gerekir. Tırnaklar "EOT"kabuk genleşme özelliklerini önler, bu nedenle girdi sözlü kullanılır. İle reado okuyacak, böylece, boş karaktere ayrılmış girdi kullanan yeni satır tek seferde metni ayrılmış. İle readarray(diğer adıyla mapfile, mevcut yana deneme-4.0) bir diziye okur ve -ther bir hat üzerindeki yeni satır şeritler.


1
readİle opsiyon here documentçok güzel! Python komut dosyalarını gömmek ve yürütmek için kullanışlıdır python -c. Daha script=$(cat <here document>)önce yapardım , ama read -r -d '' script <here document>çok daha iyi.
haridsv

9

Tüm satırların başında sekmeleri kaldıran özel bir heredoc sözdizimi var : "<< -" (tire eklendiğine dikkat edin)

http://tldp.org/LDP/abs/html/here-docs.html

Örnek 19-4 Çok satırlı mesaj, sekmeleri bastırılmış halde

Bu şekilde kullanabilirsiniz:

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"

Sonuç:

A
B
C

Boşluklarla değil, sadece sekmelerle çalışır.


Burada ubuntu'da bunun tersi doğrudur - boşluklar sekme değil çalışır. \tTabları ihtiyacınız olsa olsa harika çalışıyor. Ters eğik çizgi karakterlerini etkinleştirmek echo -e "$v"yerine sadece kullanınecho "$v"
will-ob

5

Kabuğun istenmeyen satır beslemelerini ve aşağıdaki boşlukları yemesine izin verin:

$ cat weird.sh 
#!/bin/sh

        var1="A weird(?) $(
             )multi line $(
             )text idea. $(
             )PID=$$"

        var2='You can '$(
            )'avoid expansion '$(
            )'too: PID=$$'

        var3='Or mix it: '$(
            )'To insert the PID use $$. '$(
            )"It expands to e.g. $$."

        echo "$var1"
        echo "$var2"
        echo "$var3"
$ sh weird.sh 
A weird(?) multi line text idea. PID=13960
You can avoid expansion too: PID=$$
Or mix it: To insert the PID use $$. It expands to e.g. 13960.

Bu yüzden mümkün ... ama eminim ki bu çözümü sevip sevmemek bir zevk meselesidir ...


3
bunlardan her biri bir çatal.
mikeserv

5

Belki bunu deneyebilirsin.

          echo "Test" \
               "Test2" \
               "Test3"

5

Bunu nasıl yapmanı önerdiğimi ve nedenini açıklayacağım, ama önce başka bir şey hakkında konuşmak istiyorum ...

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"

Burada sunulan diğer çözümlerin çoğu, genişletme yöntemlerinizi değiştirerek bir kabuk değişkenin içeriğini bir şekilde etkileyebileceğinizi göstermektedir. Sizi temin ederim ki bu böyle değil.

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"

ÇIKTI

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53

Yukarıda gördüğünüz, önce alan bölünmüş bir genişleme, daha sonra genişlemenin kaynak değişkeni için bayt sayısı hakkında bir rapor, daha sonra teklif sınırlandırılmış bir genişleme ve aynı bayt sayısıdır. Çıktı farklılık gösterse de, kabuk değişkeninin içeriği $stringatama hariç hiçbir zaman değişmez.

Dahası, bunun neden olduğunu anlamadıysanız, çok geçmeden sürprizlerle karşılaşmak zorunda kalacaksınız. Tekrar deneyelim, ama biraz farklı koşullarda.

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"

Aynı $string- farklı ortamlar.

ÇIKTI

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53

Alan bölme, içinde tanımlanan alan sınırlayıcılara göre gerçekleşir $IFS. İki çeşit sınırlayıcı vardır - $IFSboşluk ve $IFSbaşka şeyler. Varsayılan olarak, üç olası boşluk değeri olan $IFSdeğer alanı sekmesi newline atanır $IFS. Yine de, yukarıda gördüğünüz gibi kolayca değiştirilir ve alan bölünmüş açılımları üzerinde ciddi etkileri olabilir.

$IFSboşluk, sekansa göre tek bir alana ayrılır - ve bu, echoboşluk içeren herhangi $IFSbir boşluk sekansını içeren bir genişlemenin yalnızca tek bir boşluk olarak değerlendirmesini sağlar - çünkü echoboşluklar üzerindeki argümanlarını birleştirir. Ancak, boşluk olmayan herhangi bir değer aynı şekilde seçilmez ve her bir sınırlayıcı her zaman kendi başına bir alan alır - yukarıdaki maddelerin genişlemesinde görülebileceği gibi.

Bu en kötüsü değil. Bunu diğerini düşünün $string.

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    

ÇIKTI

* * * * * * 30
 * * *                  * * *  30

Tamam görünüyor değil mi? Peki, yine ortamı değiştirelim.

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    

ÇIKTI

file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 30
 * * *                  * * *  30

Woah.

Varsayılan olarak kabuk, dosya adı globs'unu, eğer eşleşebilirse genişletir. Bu durum sonrasında kendi ayrıştırma amacıyla parametre genişleme ve saha bölme ve böylece halka açık olmayan kısmı dize bu şekilde savunmasızdır. İsterseniz bu davranışı devre dışı bırakabilirsiniz set -f, ancak POSIX uyumlu herhangi bir kabuk varsayılan olarak her zaman değişebilir.

Bu, girinti tercihlerinize uyacak şekilde genişletmelerden alıntılar yaptığınız zaman karşı karşıya olduğunuz şeylerdir. Ve buna rağmen, her durumda, genişleme davranışından bağımsız olarak, asıl değer $stringher zaman hala en son verdiğiniz andaki değerdir . Öyleyse ilk şeye geri dönelim.

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
echo "$var" "${#var}"

ÇIKTI

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70

Bunun, kabuk sözdizimini girinti tercihlerinize uyarlamanın daha kolay bir yolu olduğuna inanıyorum. Yukarıda yaptığım şey, her bir dizgeye bir konumsal parametreye - her birinin $1ya da gibi sayılarla referans alınabilmesi ${33}- ve ardından birleştirilmiş değerlerini $varözel kabuk parametresini kullanmaya atamak $*.

Bu yaklaşım $IFS, hatta bağışıklık değildir . Yine de, $IFSbu konuda ek bir fayda ile ilişkisini düşünüyorum . Düşünmek:

IFS=\ ;space_split="$*"
IFS=/; slash_split="$*";IFS='
';new_line_split="$*"

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"

ÇIKTI

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like.
Arg 1: Line 1./Arg 2: Line 2./and so on for/as long as you might like.
Arg 1: Line 1.
Arg 2: Line 2.
and so on for
as long as you might like.

Gördüğünüz gibi $*her argümanı "$@"ilk baytta birleştirir $IFS. Dolayısıyla $IFS, farklı atanmış iken değerini kaydetmek, kaydedilen her değer için farklı alan sınırlayıcıları alır. Yukarıda gördüğünüz, bu arada, her değişken için gerçek değerdir. Hiç sınırlayıcı istemiyorsanız, şunları yaparsınız:

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"

ÇIKTI

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67

2

Denemek isteyebilirsiniz:

echo $VAR | tr -s " "

veya

myArr=($VAL)
VAL=${myArr[@]}
echo "$VAL"

ve ayrıca kontrol edebilirsiniz bu dışarı.


2

Bash Değiştirme Genişlemesi kullanın

Bash kullanıyorsanız, bir ikame genişletme kullanabilirsiniz . Örneğin:

$ echo "${VAR//  /}"
This displays with extra spaces.

1

Bu, yol benzeri değişkenleri ayarlamak için bir değişkendir:

set -- "${MYDIR}/usr/local/lib" \
      :"${MYDIR}/usr/lib" \
      :"${MYDIR}/lib" \
       "${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
    export LD_LIBRARY_PATH="$*"
    LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)

Daha sonra aşağıdaki şekilde kaydedilebilecek ve kullanılabilecek olan setüzerine yazmaların $@kullanılması:

ARGV=("$@")
exec foo "${ARGV[@]}"

LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)Hat iki nokta üst üste hem de mümkün olan arka boşluk önce boşluk ortadan kaldırır. Yalnızca sondaki boşlukları ortadan kaldırıyorsanız, LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }bunun yerine kullanın.

Bütün bu yaklaşım mikeserv'in mükemmel cevabı üzerine bir değişken.


0

Boşluk karakterlerinden korkma. Çok satırlı metninizi yazdırmadan önce bunları kaldırın.

$ cat ./t.sh
#!/bin/bash

NEED_HELP=1
if [[ $NEED_HELP -eq 1 ]]; then

    lucky_number=$((1 + RANDOM % 10 + 10))

    read -r -d '' text <<'    EOF'
    NAME says hello

    Look at this helpful text:

                                                 * -**
                                 Eyjafjallajokull        Eyjafjallajokull
                            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

    Don't go away, please rate your experience... It'll just take two minutes.

    EOF
    text=$(echo "$text" | sed -r 's!^\s{4}!!')
    text=$(echo "$text" | sed -r "s!\bNAME\b!$0!") # Bash: text=${text//NAME/$0}
    text=$(echo "$text" | sed -r "s!\btwo\b!$lucky_number!")

    echo "$text"

fi

Çıktı:

$ ./t.sh
./t.sh says hello

Look at this helpful text:

                                             * -**
                             Eyjafjallajokull        Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

Don't go away, please rate your experience... It'll just take 16 minutes.

<<-Heredoc kullanmanıza ve 4 boşluklu girintinizi sekme karakterleriyle kırmanıza gerek yok .

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.