web-dev-qa-db-ja.com

インデントされている間に複数行にわたって文字列値を変数に割り当てる方法は?

問題:

  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.

これらのスペースなしでエレガントに割り当てるにはどうすればよいですか?

66
JamesL

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

    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"

ただし、以下のシナリオの場合、echoHelloおよびWorldの2つの引数を取得します。

$ variable="Hello World"
$ echo $variable
35
Kannan Mohan

esuoxuおよびMickaëlBucasによって与えられるソリューションは、これを行うための一般的で移植性の高い方法です。

ここにいくつかのbashソリューションがあります(zshのような他のシェルでも機能するものもあります)。まず、+=追加演算子を使用します(整数変数、通常の変数、配列のそれぞれに対して少し異なる方法で機能します)。

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をフォーマット文字列内に置くか、$ ''(または両方)を使用して空白を処理できます。

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

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

ただし、テキストコンテンツ全体を一度に表示する場合は、配列を「フラット化」することを忘れないでください。

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

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

最後に、readまたはreadarrayと「ヒアドキュメント」を使用します。

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は各行の改行を削除します。

31
mr.spuratic

すべての行の先頭にあるtabsを削除する特別なヒアドキュメント構文があります: "<<-"(ダッシュが追加されていることに注意してください)

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

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

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

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

結果:

A
B
C

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

13
Mickaël Bucas

これは私があなたがそれをするべきだと提案する方法です、そして私は理由を説明します、しかし最初に私は何か他のものについて話したいです...

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に値space tab newline-が割り当てられます。これらは、3つの可能な$IFS空白値です。ただし、上記のように簡単に変更でき、フィールド分割展開に大きな影響を与える可能性があります。

$IFSホワイトスペースはsequenceによって単一のフィールドに移動します-これが、$IFSにスペースが含まれているときに、スペースのシーケンスを含む展開をechoingすると評価される理由です単一のスペースのみに-echoはスペースの引数を連結するため。しかし、空白以外の値は同じように省略されず、出現する各区切り文字は常にそれ自体にフィールドを取得します-上記のstuff展開でわかるように。

これは最悪ではありません。この他の$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

わあ。

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

これは、インデントの設定に合わせて、展開に引用符を付けるときに直面する類のことです。それでも、すべての場合において、展開動作に関係なく、$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

これは、シェルの構文をインデントの設定に適合させるためのはるかに健全な方法だと思います。上記で私がしていることは、個々の文字列をそれぞれ$1${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
5
mikeserv

シェルに不要な改行と次のスペースを食べさせます。

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

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

5
user62916

多分これを試すことができます。

          echo "Test" \
               "Test2" \
               "Test3"
4
esuoxu

あなたが試してみたいかもしれません:

echo $VAR | tr -s " "

または

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

また、 this をチェックアウトすることもできます。

2
Vivian Maya

Bash置換拡張を使用する

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

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

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

_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の優れた答えの変形です。

1
rsanden

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

$ 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文字のインデントをタブ文字で区切る必要はありません。

0
c0xc