余分なスペースがある複数行の文字列(インデントは保持されます)


410

以下の定義済みテキストをファイルに書き込みたい:

text="this is line one\n
this is line two\n
this is line three"

echo -e $text > filename

私はこのようなものを期待しています:

this is line one
this is line two
this is line three

しかしこれを得た:

this is line one
 this is line two
 this is line three

それぞれの後にスペースがないと確信して\nいますが、余分なスペースはどのように出てきますか?


2
よくわかりませんがtext="this is line one\nthis is line two\nthis is line three"、同じ1行を入力した場合はどうなりますか。(エンターなし)
Yohanes Khosiawan许先汉2014年

4
\n各行のを削除すると、新しい行に移動するためにすでに改行がヒットします
Mark Setchell、2014年

あなたはすでに与えました。\nそれであなたはなぜ次の行を新しい行に入れるのですか?単にtext="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi 2014年

1
\n各行の終わりにあるを削除すると、出力がすべて1行にまとめて実行されます。
Jonathan Hartley、

18
Aha:"$text"エコー行のを二重引用符で囲むことが重要です。それらがなければ、改行(リテラルと '\ n'の両方)は機能しません。彼らと一緒に、彼らはすべて行います。
Jonathan Hartley

回答:


663

ヘレドックはこの目的のためにより便利に聞こえます。複数のコマンドをexcatなどのコマンドインタープリタープログラムに送信するために使用されます。

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

後の文字列<<は、停止する場所を示します。

これらの行をファイルに送信するには、次を使用します。

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

これらの行を変数に格納することもできます。

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

これは、行をという名前の変数に格納しますVAR

印刷するときは、変数を囲む引用符に注意してください。そうしないと、改行文字が表示されません。

echo "$VAR"

さらに良いことに、インデントを使用して、コード内でより目立つようにすることができます。今回は、タブが表示されないようにするために-after <<を追加します。

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

ただし、コードのインデントにはスペースではなくタブを使用する必要があります。


38
なぜ2番目と5番目のコードブロックの最初の行に<<の代わりに<<-があるのですか?は-何か特別なことをしますか?
lohfu

35
@ sup3rman '-'先頭のタブを無視します。stackoverflow.com/questions/2500436/…を
カービン

8
ステータス0で読み取りを終了するにはどうすればよいですか?それは行末を見つけることができないようで(-d ''であるため)、スクリプトを実行しているときに役に立たない1で終了します。
Misha Slyusarev 2015年

7
@MishaSlyusarevは-d '\ 0'を使用し、最後の行の終わりに\ 0を追加します
Lars Schneider

11
-dオプションを使用してもread -r -d '' VARIABLE <<- EOMうまくいきませんでした。
dron22

186

文字列を変数に入れようとしている場合、別の簡単な方法は次のようなものです。

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

タブで文字列をインデントすると(つまり、 '\ t')、インデントは取り除かれます。スペースでインデントすると、インデントはそのまま残ります。

注:最後の閉じ括弧が別の行にあること重要です。ENDテキストは単独行に表示されなければなりません。


1
それは重要ですか?Macで)同じ行で動作します。私は間で何が起こっているため、それはだと思う$(し、)とにかく「)」独自の宇宙で実行するつもりであり、実際のコマンドが表示されません。他の人でもうまくいくかしら。
deej

シェルによって解釈が異なる可能性があります。bashのドキュメントでは、どちらかと言えば何も言われていないようですが、すべての例では独自の行に記載されています。
Andrew Miner 2017年

面白いのは、USAGE変数にも値を設定しようとしたときに答えがわかったということです。だからあなたの答えは正確に一致します。:)
Bunyk 2018年

1
@deej POSIX仕様から:区切り記号と<改行>のみを含む行があるまで、ヒアドキュメントは続きます
Fornost

1
@Fornostそれは私が言ったことと矛盾しない
deej

84

echo渡された引数の間にスペースを追加します。$textは変数展開と単語分割の対象となるため、echoコマンドは次と同等です。

echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n"  ...

「this」の前にスペースが追加されていることがわかります。改行文字を削除し、引用符で$text改行を保持できます。

text="this is line one
this is line two
this is line three"

echo "$text" > filename

またはprintf、次のものよりも堅牢で移植性のあるを使用することもできますecho

printf "%s\n" "this is line one" "this is line two" "this is line three" > filename

ではbashブレース展開をサポートし、あなたも行うことができます:

printf "%s\n" "this is line "{one,two,three} > filename

1
「echo」コマンドの使用時にmodが余分なスペースを認識する理由を説明していただきありがとうございます。この答えは、bashスクリプトで使用する場合にも有効です(インタラクティブなシェルセッションではなく)。これが本当の答えであり、受け入れられた答えである必要があると感じます。
onelaview

1
これは受け入れられるべき答えです。他のすべての答えはOPの痛みを解決するための興味深い他の方法を提供しましたが、技術的には「余分なスペースがどうやって出てくるのか」という問題でした。誰もこれに対処しませんでした:-) !!! Josh、-1のミステリーを作成していただきありがとうございます。
Dmitry Shevkoplyas

45

bashスクリプトでは、次のように機能します。

#!/bin/sh

text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename

あるいは:

text="this is line one
this is line two
this is line three"
echo "$text" > filename

猫のファイル名は:

this is line one
this is line two
this is line three

シェルスクリプトでは代替策が機能するようですが、bashでは機能しないようです。
Macindows

3
@Macindowsの-eオプションを忘れてしまったようですecho。もう一度やり直してください。
Chris Maes

41

すべての行を適切にインデントしたいので、もっと多くの解決策を見つけました:

  1. 以下を使用できますecho

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename

    行の終わりの"\n"直前に置くと機能しません\

  2. 別の方法printfとして、移植性を向上させるためにを使用することもできます(たまたまで多くの問題が発生しましたecho)。

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. さらに別の解決策は次のとおりです。

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this is line three\n"
    printf "%b" "$text" > filename

    または

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
  4. 別の溶液を混合することによって達成されるprintfsed

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi

    インデントレベルをコードにハードコーディングするため、このようにフォーマットされたコードをリファクタリングするのは簡単ではありません。

  5. ヘルパー関数といくつかの変数置換トリックを使用することが可能です:

    unset text
    _() { text="${text}${text+
    }${*}"; }
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"

3bは、コードのインデントと文字列のインデントを個別に保持したい場合、つまり、少なくとも変数の割り当てで2を使用できず、3aよりも読みやすい場合に推奨されるソリューションです。気にしないときは、引用符で囲まれた文字列を数行にわたって開いたままにしておきます。
Pysis

非常に良い答え、特に3b
Sumit Trehan

「行末の\の直前に\ nを置いても機能しません。」- エコーを使用する場合のヒントです。
Noam Manos

6

以下は、複数行の文字列を変数に割り当てるための推奨される方法です(見た目は良いと思います)。

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

アンダースコアの数はどちらの場合も同じです(ここでは80)。


0

単純な1行の連結について言及するだけで、時々役立つ場合があります。

# for bash

v=" guga "$'\n'"   puga "

# Just for an example.
v2="bar "$'\n'"   foo "$'\n'"$v"

# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar ${n}   foo ${n}$v"

echo "$v3" 

あなたはこのようなものを得るでしょう

バー 
   foo 
 グガ 
   プーガ 

先頭と末尾のすべての空白はそのまま保持されます

echo "$v3" > filename

0

それを行うには多くの方法があります。私にとって、インデントされた文字列をsedにパイプすることはうまくいきます。

printf_strip_indent() {
   printf "%s" "$1" | sed "s/^\s*//g" 
}

printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"

この回答はMateusz Piotrowskiの回答に基づいていますが、少し改良されています。


-1

次のように配置すると機能します。

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.