「env」コマンドで改行で値を設定するには?

「env」コマンドで改行で値を設定するには?

私の言葉は、env次の形式のGNUまたはBSDコマンドを使用していることです。env

env [name=value ...] [utility [args ...]]

特殊文字を部分的にエスケープする方法はないようですが、その部分を解析するvalue方法がわかりません。envvalue

envシェルの機能を使ってこれを行う方法はいくつかありますが、シェルの助けを借りずにリテラル文字列を渡したいと思います(execute byと似ていますexec)。つまり、コマンドでサポートされている改行文字を含むいくつかのenvリテラル文字列形式を見つける必要があります。例えば:

env FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'

これにはLITERAL_STRING改行文字を含むリテラル文字列を含める必要があり、env形式を理解する必要があります。

上記のコマンドを使用すると、予想される出力は次のようになります。

hello
world

可能かどうか知りたいです。ご協力ありがとうございます。

環境

  • env

    BSDを使用しているため、envバージョンを印刷できません。これがman役に立つかどうかはわかりません。

    $ man env | tail -n1
    BSD                             April 17, 2008                             BSD
    
  • オペレーティングシステム

    $ sw_vers
    ProductName:    macOS
    ProductVersion: 11.6
    BuildVersion:   20G165
    

答え1

あなたの理解が間違っています。"hello\nworld"変数に何かを保存すると、\n文字通り解釈されます。printfまたは、フラグechoなどのツールを呼び出す場合にのみ-eコンソールに印刷するときに、これらのバックスラッシュシーケンスを展開します。

あなたの場合、変数を環境に渡して改行を拡張したい場合は、次を使用することをお勧めします。ANSI Cスタイル$'...'参照演算子ksh93 バージョンは、bash や zsh を含むいくつかの異なるシェルでサポートされるようになりました。

env FOO=$'hello\nworld' ruby -e 'puts ENV["FOO"]'

あるいは、移植性が問題の場合、簡単な解決策は目的の場所に改行文字を配置することです。

f="hello
world"

env FOO="$f" ruby -e 'puts ENV["FOO"]'

答え2

牛に似た一種の栄養そしてFreeBSD env-S引数を空白に分割し、結果文字列に次のものを含むがこれらに限定されない多数のエスケープ文字を解釈する(「分割」)オプション[1]があります\n

$ env -S 'foo=bar\nquux printenv foo'
bar
quux
$ env -S 'foo=bar\nquux' printenv foo
bar
quux

[1]デフォルトの用途はshebangライン用ですが、コマンドラインが必要な他の場所でも使用できますが、ほとんどのvteベースの端末オプション#! ...と同様に、これを解釈するためにシェルの代わりに一時パーサを使用します。-e

答え3

このenvコマンドは、実行時にシステムコールから複数のパラメータを取得します(他のコマンドと同様)execve()。これを実行するプロセスは、次のことを行います。

execve("/path/to/env", ["env", "-options", "var=value", "cmd", "arg"], environ);

順次実行されますenv(通常、同じプロセス内で)。

execve("/path/to/cmd", ["cmd", "arg"], modified_environ);

どこmodified_environにあるががenviron追加されているか、inで始まる文字列がvar=valueすでに1つ以上ある場合、その最初の文字列は(少なくとも)に置き換えられます。var=environvar=value

これらのパラメータはNULで終わるバイト配列なので、含めることができない唯一のバイトはNULバイトです。名前と値にNULバイトを含めることができない環境変数にも同じことが当てはまります。

env起動時に、そのパラメータは最初から最後まで処理されます。

で始まるものは-オプションと見なされます。

-一人でも古代の形で-i無視されますenviron

--少なくとも1つを含む(またはオプションの終わりを表示するために使用できる)次のオプションは、=環境変数文字列(上に配置されている)として扱われますmodified_environ。文字を含まないオプションではない最初の引数は=コマンド名として扱われます(最初の引数としてコマンドに渡され、クエリ内の実行可能ファイルへのパスとしても使用されます$PATH)。

-これ以降のすべての引数は、文字で始まったり文字を含めたりしても、コマンドの引数として渡されます=

したがって、文字だけがenvそれ自体で特別です。オプション処理中にのみ、コマンド名を見つける前にのみ可能です。-=-=

-で始まる環境変数名またはで始まるコマンド名を渡すには、-次のようにします--

execve("/usr/bin/env", ["env", "--", "-var-=value", "cmd"], environ);
execve("/usr/bin/env", ["env", "--", "-cmd-"], environ);

複数envの実装では-、次のものは--まだコマンド名とは見なされません。したがって、名前付きコマンドを呼び出す前に、-1つ以上の環境変数を渡す必要があります。

execve("/usr/bin/env", ["env", "--", "dummy=", "-", "args"], environ);

(または代わりに、または/path/to/-コマンドのパスルックアップを無視して使用してください)。./--$PATH-

env=ただし、名前に文字が含まれて=渡されないコマンドは実行できませんmodified_environ=この場合、この役割を回避する方法はありません。

しかし、あなたの場合、NULバイトを除いてenv FOO=LITERAL_STRING文字自体は問題ではありません。LITERAL_STRINGenv

このコマンドを実行するために言語でコードを書くとき、envその言語には文字通り入力できない文字がある可能性があります。

たとえば、perl次のことができます。

exec "env", "--", "-text-=hello\nworld\n", "printenv", "--", "-text-";

改行文字を二重引用符で囲んで表現します\nが、次のようにすることもできます。

exec qw(env --), q(-text-=hello
world
), qw(printenv -- -text-);

改行文字を含む文字列を渡します。

Bourneに似たシェル言語の構文では、改行文字は一重引用符または二重引用符(たとえば、q(...)参照qq(...)演算子perl)の中にある場合は特別ではないため、次のようにすることができます。

exec env -- "-text-=hello
world
" printenv -- -text

"..."参照演算子が使用されていないCシェルまたはrcシェルでは、状況が異なります。

あなたのenv FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'コードはシェル言語のコードのように見えます。ほとんどすべてのシェルがスペースを単語区切り文字と'...'引用演算子として理解するため、この構文はほとんどのシェルに有効です。

exec最初にこれを省略すると、シェルは子プロセスでコマンドを実行し、そのプロセスが終了または中断されるのを待ちます。

これが改行文字を文字通り(おそらくシェルではない)入力したり、何らかの形のエンコーディング(、、、...)を許可しないが、ほとんどのシェルのような引用演算子を明確にサポートするenv FOO=LITERAL_STRING ruby -e 'puts ENV["FOO"]'言語であれば、BSDがすでにトリックを実行することをを除いて\n%0a
'...'env -S@UncleBillyが提案しましたperl、コマンドを実行したり、、、、、、...などの一部のエンコーディングで改行文字を指定できる言語rubyまたはインタプリタを呼び出すことができます。pythonksh93zshbash

すでに持っているのでruby

ruby -e 'exec "env", "FOO=a\n\n\nb", *ARGV' -- ruby -e 'puts ENV["FOO"]'

ただし、すべての系譜プログラミング言語はruby独自に環境変数を設定できるため、以下を行う必要はありませんenv

ruby -e 'ENV["FOO"]="a\n\n\nb"; exec *ARGV' -- ruby -e 'puts ENV["FOO"]'

答え4

値にリテラル改行を使用します。

$ env FOO="hello                                  
world" ruby -e 'puts ENV["FOO"]'

出力:

hello
world

文字列を実際の改行文字に変換するには、\n次のユーティリティを使用しますprintf

$ env FOO="$(printf "%b" 'hello\nworld')" ruby -e 'puts ENV["FOO"]'
hello
world

envこれに関して特別なことはありません(実際のenvプロセスが開始される前に拡張されたため、実際の改行文字のみが表示されます)。

関連情報