インデントしながら複数行にわたって変数に文字列値を割り当てる方法は?


54

問題:

  1. 変数に適切な長さの値を割り当てる必要があります。
  2. スクリプトのすべての行は、特定の列数の下になければなりません。

そのため、複数の行を使用して割り当てようとしています。

インデントなしで行うのは簡単です:

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

結果:

This displays without any issues.

ただし、インデントがある場合:

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

結果:

This displays with      extra spaces.

これらのスペースなしでどのようにエレガントに割り当てることができますか?

回答:


31

ここでの問題は、変数を二重引用符( "")で囲んでいることです。削除すると問題なく動作します。

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

出力

 This displays with extra spaces.

ここでの問題は、変数を二重引用符で囲むと、すべての空白文字が保持されることです。これは、明示的に必要な場合に使用できます。

例えば、

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

印刷します

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

引用符を削除すると、その異なる

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

ここで、Bashはテキストの余分なスペースを削除します。これは、最初のケースでは、テキスト全体が「単一の」引数として使用され、余分なスペースが保持されるためです。ただし、2番目の場合、echoコマンドは5つの引数としてテキストを受け取ります。

コマンドに引数を渡すときに変数を引用することも役立ちます。

以下のコマンドでechoは、単一の引数のみを取得します"Hello World"

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

しかし、以下のシナリオの場合にはecho、2つの引数を取得Helloし、World

$ variable="Hello World"
$ echo $variable

ほとんどの回答はうまくいきましたが、これが最も簡単でした。ありがとう!
Sman865

12
@ Sman865-これが実際にほぼ間違いなくここで提供される最も複雑な答えであり、ほとんどの点で間違っていることを教えてください。値の割り当てを取り巻く問題は、その後の拡張とは一切関係ありません。申し訳ありませんが、カンナンですが、この答えは間違っていて、間違っています。フィールド分割拡張$IFSは強力で普遍的なツールですが、この方法で記述されたコードは、いかなる種類の信頼できる結果も決して生成できません。
mikeserv 14年

2
変数を展開するときに引用符を使用しないと、遅かれ早かれ、ファイル名の展開(のいずれか* ? [])から始まる問題が発生します。
Mr.spuratic 14年

bashの展開を防ぐために、変数を二重引用符で囲む必要があることに同意します。しかし、特別な場合には、コードの複雑化を防ぐためにそれを避けることができます。
Kannanモハン

1
私は@mikeservに同意します。これは額面通りに取られた場合、非常に悪いアドバイスです。結果なしで使用すると、これは壊れます。たとえば、変数がコマンドの引数として渡される場合、各単語を個別の引数として分割することは望ましくありません。
haridsv

28

与えられたソリューションesuoxuミカエル・Bucasは共通して、これをより移植性の高い方法です。

以下にbashいくつかの解決策を示します(そのうちのいくつかは、などの他のシェルでも機能するはずですzsh)。まず、+=append演算子を使用します(整数変数、通常の変数、配列のそれぞれに対してわずかに異なる方法で機能します)。

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

テキストに改行(または他の空白/エスケープ)が必要な場合は、$''代わりに引用符を使用します。

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

次に、printf -vフォーマットされた値を変数に割り当てるために使用

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

ここでのコツは、書式指定子よりも多くの引数があるため、ほとんどのprintf関数とは異なり、bashは書式文字列がなくなるまで再利用します。\n書式文字列内にaを入れるか、$ ''(またはその両方)を使用して空白を処理できます。

次に、配列を使用します。

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

を使用+=して、テキストを1行ずつ作成することもできます(注意してください()):

text+=("post script")

ただし、テキストコンテンツ全体を1回で実行する場合は、配列を「フラット化」することを忘れないでください

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

(連想配列とは異なり、整数のインデックス付き配列は暗黙的にソートされます)これにより、必要に応じて行を操作したり、スライスとダイスを実行したりできるため、柔軟性がわずかに向上します。

最後に、readまたはreadarray「here-document」を使用します。

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  

ヒアドキュメント形式で<<-は、すべての主要なハードタブが入力から削除されるため、タブを使用してテキストをインデントする必要があります。引用符は"EOT"シェル拡張機能を妨げるため、入力は逐語的に使用されます。ではread、それはNULバイトは、それが一度に改行区切りのテキストを読み込みますので、入力を区切り使用しています。readarray(別名mapfileのbash-4.0以降、利用可能な)それが配列に読み込み、-t各行に改行を取り除き。


1
とのreadオプションhere documentはとてもいいです!Pythonスクリプトを埋め込み、で実行するのに便利python -cです。script=$(cat <here document>)以前はやっていましたread -r -d '' script <here document>が、ずっといいです。
haridsv

9

すべての行の先頭にあるタブを削除する特別なheredoc構文があります: "<<-"(ダッシュが追加されたことに注意してください)

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

例19-4。タブが抑制された複数行メッセージ

次のように使用できます。

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

結果:

A
B
C

スペースではなくタブでのみ機能します。


ここubuntuでは、逆のことが言えます。タブではなくスペースが機能します。\tただし、タブが必要な場合はうまく機能します。ただ、使用echo -e "$v"ではなく、echo "$v"バックスラッシュ文字を有効にする
意志-OB

5

シェルが不要な改行と次のスペースを食べるようにします。

$ 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.

だからそれは可能です...しかし、このソリューションを好きか嫌いかは好みの問題です...


3
これらはすべてフォークです。
mikeserv 14年


5

これをどうやって行うべきかを提案し、その理由を説明しますが、最初に何か他のことについてお話したいと思います...

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

ここで提供されている他の多くのソリューションは、展開方法を変更することにより、シェル変数の内容に何らかの形で影響を与えることができることを示唆しているようです。これは事実ではないことを保証できます。

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

出力

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

上記の内容は、最初にフィールド分割展開、次に展開のソース変数のバイト数に関するレポート、次に引用符で区切られた展開、および同じバイト数です。出力は異なる場合がありますが、シェル変数の内容は$string割り当て時を除いてまったく変化しません。

さらに、これがなぜなのか理解していないと、非常に厄介な驚きにすぐに遭遇することになります。少し条件を変えて、もう一度試してみましょう。

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

同じ$string-異なる環境。

出力

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

フィールド分割は、で定義されたフィールド区切り文字に基づいて発生し$IFSます。区切り$IFS文字には、空白と$IFSその他の2種類があります。デフォルトで$IFSは、値スペースのタブ改行が割り当てられ$IFSます。これは、3つの可能な空白値です。ただし、上記のように簡単に変更でき、フィールド分割拡張に大きな影響を与える可能性があります。

$IFS空白はシーケンスによって単一のフィールドに移動します。これはecho、スペースを$IFS含むときにスペースのシーケンスを含む展開が単一のスペースのみと評価される理由echoです。これは、スペースで引数を連結するためです。しかし、空白以外の値は同じように変化せず、発生する区切り文字のそれぞれは常にフィールドを取得します- 上記のもので見られるように。

これは最悪ではありません。この他を検討してください$string

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

出力

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

良さそうですね。さて、もう一度環境を変えましょう。

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

出力

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

わあ

デフォルトでは、シェルはファイル名が一致する場合、ファイル名を展開します。これはパラメーターの展開と解析順序のフィールド分割の後に発生するため、引用符で囲まれていない文字列はこの方法で脆弱です。set -f必要に応じてこの動作をオフに切り替えることができますが、POSIX互換シェルはデフォルトで常にglobになります。

これは、インデントの設定に合わせて展開に引用符をドロップするときに直面する種類です。$stringそれでも、どのような場合でも、展開動作に関係なく、実際の値は常に最後に割り当てたときの値のままです。それでは、最初の話に戻りましょう。

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

出力

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

これは、シェル構文をインデント設定に適合させるはるかに安全な方法だと思います。上記で行っていることは、個々の文字列を位置パラメータに割り当てます。位置パラメータは、$1orなどの数字で参照できます。${33}次に、連結値を$var特別なシェルパラメータを使用して割り当てます$*

このアプローチは$IFS、たとえそうであっても影響を受けません。それでも、私$IFSはこの点で追加の利益との関係を検討します。考慮してください:

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

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

出力

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.

ご覧のとおり、の最初のバイトで$*各引数を連結"$@"$IFSます。そのため、$IFS異なる割り当てが行われている間に値を保存すると、保存された値ごとに異なるフィールド区切り文字が取得されます。ちなみに、上の図は各変数のリテラル値です。区切り文字がまったく必要ない場合は、次のようにします。

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

出力

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

2

あなたが試してみたいことがあります:

echo $VAR | tr -s " "

または

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

そしてまた、あなたがチェックすることができ、これを行います。


2

Bash Substitution Expansionを使用する

Bashを使用している場合、置換展開を使用できます。例えば:

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

1

これは、パスのような変数を設定するためのバリアントです。

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)

set上書きを使用して$@、保存して後で次のように使用できます。

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

このLD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)行は、コロンの前のスペースと後続の空白を削除します。末尾のスペースのみを削除する場合は、LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }代わりに使用します。

このアプローチ全体は、mikeservの優れた答えの変形です。


0

空白文字を恐れないでください。複数行のテキストを印刷する前にそれらを削除してください。

$ 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

出力:

$ ./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.

<<-ヒアドキュメントを使用して、タブ文字で4スペースのインデントを解除する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.