shの文字列に改行を含めるにはどうすればよいですか?


337

この

STR="Hello\nWorld"
echo $STR

出力として生成する

Hello\nWorld

の代わりに

Hello
World

文字列に改行を入れるにはどうすればよいですか?

注:この質問はエコーに関するものではありません。 私は知っecho -eていますが、を改行として解釈する同様のオプションを持たない他のコマンドに引数として文字列(改行を含む)を渡すことができる解決策を探しています\n

回答:


353

解決策は$'string'、たとえばを使用することです。

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

以下は、Bashのマニュアルページからの抜粋です。

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.

74
それは有効なbashですが、POSIX shではありません。
jmanning2k

7
+ 1-just a note:export varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev

3
これは確かに、OPが要求したとおりにリテラル文字列で機能しますが、変数が置き換えられるとすぐに失敗します。STR=$"Hello\nWorld"単に出力しHello\nWorldます。
ssc

3
@ssc二重引用符ではなく単一引用符
miken32

7
@ssc例には変数がありません。また、この方法では一重引用符が必要です。最後に、補間された変数を含めるには、二重引用符で囲まれた文字列と特殊な単一引用符で囲まれた文字列を連結する必要があります。たとえば、H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"「Hello」と「World」を別々の行にエコーします
JDS

166

エコーは非常に90年代であり、非常に危険に満ちているので、その使用は4GB以上のコアダンプをもたらすはずです。真剣に、エコーの問題は、Unix標準化プロセスが最終的にprintfユーティリティを発明し、すべての問題を排除した理由でした。

文字列の改行を取得するには:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

そこ!SYSV対BSDエコーの狂気はなく、すべてがきちんと出力され、Cエスケープシーケンスの完全に移植可能なサポートが提供されます。みなさんprintf今すぐ使ってください、決して振り返ってはいけません。


3
この回答をありがとう、それは本当に良いアドバイスを提供すると思います。ただし、私の元の質問は、エコーを取得して改行を出力する方法ではなく、改行をシェル変数に取得する方法に関するものでした。printfを使用してそれを行う方法も示していますが、amphetamachineを使用した解決策はより$'string'クリーンだと思います。
ファンA.ナバロ

11
「クリーン」の定義が不十分な場合のみ。$'foo'構文は文句を言うでしょう2013年、多くの貝殻の有効なPOSIX構文ではありません。
イェンス

5
BAR=$(printf "hello\nworld\n")私の末尾の\ nを出力しません
ジョニー2013年

3
@ジョニーそれはすべきではない。コマンド置換のシェル仕様は、出力の最後にある1つ以上の改行が削除されることを示しています。これは私の例では予想外であることに気づき、printfに改行を含めるように編集しました。
イェンス

5
@ismaelいいえ、置換の最後にある1つ以上の< newline >文字のシーケンスを削除すること$()としてPOSIXで指定されています
Jens

61

他の答えに基づいて私がしたことは

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight

29

問題はシェルにありません。問題は実際にはechoコマンド自体にあり、変数の補間を囲む二重引用符がありません。使用してみることができますがecho -e、すべてのプラットフォームでサポートされているわけではなくprintf、移植性のために理由の1つが推奨されています。

改行をシェルスクリプトに直接挿入して(スクリプトが作成中の場合)、次のようにすることもできます...

#!/bin/sh
echo "Hello
World"
#EOF

または同等に

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!

23

-e旗はエレガントでわかりやすいと思います

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

文字列が別のコマンドの出力である場合、引用符を使用します

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"

2
これはすでに他の回答で言及されています。また、すでにそこにありましたが、この質問はについてではないという事実を強調するために質問を編集しましたecho
Juan A. Navarro

これは質問に対する回答ではありません
Rohit Gupta

これは私が探していた質問に完全に答えます!ありがとう!
キエーヴェリ2015年

1
ここで私のためにうまくいきました。私はこのエコーに基づいてファイルを作成するために使用し、出力に新しい行を正しく生成しました。
マテウスレオン2016

echo -e移植性の問題で悪名高い。これは現在、POSIX以前よりもワイルドウエストの状況ではありませんが、優先する理由はまだありませんecho -estackoverflow.com/questions/11530203/…
tripleee

21
  1. 唯一の簡単な方法は、実際に変数に新しい行を入力することです。

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line

    はい、それはEnterコードの必要な場所に書き込むことを意味します。

  2. new lineキャラクターと同等のものがいくつかあります。

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.

    しかし、それらすべては何らかのツール(POSIX printf)による「解釈」を必要とします:

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    したがって、ツールは改行付きの文字列を作成する必要があります。

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. 一部のシェルでは、シーケンス$'は特別なシェル拡張です。ksh93、bash、zshで動作することが知られています:

    $ STR=$'new\nline'
  4. もちろん、より複雑なソリューションも可能です:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    または

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

6

次のように、単一引用符の直前の$は '... \ n ...'ですが、二重引用符は機能しません。

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld

4

私はbashの専門家ではありませんが、これは私にとってはうまくいきました:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

これはテキストをフォーマットする方が簡単だと思いました。


2
古き良きヒアドキュメント。そして、それはおそらくPOSIXにも準拠しています!
pyb 2017

引用符は、周り$NEWSTRではecho "$NEWSTR"、ここで重要な
シャディ

0

私のシステム(Ubuntu 17.10)では、コマンドライン(into sh)から入力した場合とshスクリプトとして実行した場合の両方で、例は期待どおりに機能します。

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

これはあなたの質問に答えると思います:それはうまくいきます。(私は、改行文字の置換が正確にどの時点で発生するかなどの詳細を把握しようとしませんでし\nsh)。

ただし、この同じスクリプトをで実行するbashと動作が異なり、Hello\nWorld代わりに出力されることに気付きました。

[bash bash test-str.sh
Hello\nWorld

私はbash次のようにして目的の出力を得ることができました:

[bash STR="Hello
> World"
[bash echo "$STR"

を囲む二重引用符に注意してください$STR。これを保存してbashスクリプトとして実行すると、同じように動作します。

次の例でも、望ましい出力が得られます。

[bash echo "Hello
> World"

0

改行だけを必要とし、インデントを解除する複数行のコードを軽視するうるさいものは、次のようになります。

IFS="$(printf '\nx')"
IFS="${IFS%x}"

Bash(および他のシェル)はコマンド置換後にすべての末尾の改行をむちゃくちゃにするので、printf文字列を改行以外の文字で終了し、後で削除する必要があります。これも簡単にワンライナーになります。

IFS="$(printf '\nx')" IFS="${IFS%x}"

私はこれが1つのアクションではなく2つのアクションであることを知っていますが、私のインデントと移植性のOCDは現在安定しています:)私は元々、改行のみの分離された出力を分割できるようにこれを開発\rし、終了文字として使用する変更を使用してしまいました。これにより、末尾がで終わるdos出力でも、改行の分割が機能します\r\n

IFS="$(printf '\n\r')"

0

ここにあるオプションのどれにも満足していませんでした。これは私のために働いたものです。

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")

これは非常に退屈な記述方法ですstr=$(printf '%s\n' "first line" "another line" "and another line")(シェルはコマンドの置換から最終的な改行を削除する(またはしない)ので便利です)。ここのいくつかの回答はすでにprintfさまざまな形で宣伝されているので、これが別の回答として価値を追加するかどうかはわかりません。
tripleee

短い文字列用ですが、整頓したい場合、各行が実際には60文字の長さであれば、整然とした方法で行うことができます。それが私が最終的に行った解決策であることを考えると、それはあなたのためではないにしても、将来私が間違いなく戻るときに私自身のために何かを追加します。
アダムKディーン

1
長い文字列の場合でもprintf '%s\n' "first line" \ (改行、インデント)'second line'などを好みますprintf -v str。サブシェルを生成せずに変数に割り当てる方法も参照してください。
tripleee
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.