次の行でプログラムを書いたとしましょう。
int main(int argc, char** argv)
これで、argv
の内容を確認することで、どのコマンドライン引数が渡されるかがわかります。
プログラムは引数間のスペースをいくつ検出できますか?私がこれらをbashでタイプするときのように:
ibug@linux:~ $ ./myprog aaa bbb
ibug@linux:~ $ ./myprog aaa bbb
環境は最新のLinux(Ubuntu 16.04など)ですが、POSIX準拠のシステムすべてに当てはまるはずです。
「引数の間のスペース」について話すことは意味がありません。それがシェルのコンセプトです。
シェルの仕事は、入力の行全体を取り、それらを引数の配列に形成して、コマンドを開始することです。これには、引用符で囲まれた文字列の解析、変数の展開、ファイルのワイルドカードとチルダ式などが含まれます。コマンドは、文字列のベクトルを受け入れる標準のexec
システムコールで開始されます。
文字列のベクトルを作成する方法は他にもあります。多くのプログラムは、事前に定義されたコマンド呼び出しで独自のサブプロセスをフォークして実行します。その場合、「コマンドライン」などは存在しません。同様に、ユーザーがファイルアイコンをドラッグしてコマンドウィジェットにドロップすると、グラフィカル(デスクトップ)シェルがプロセスを開始する可能性があります。ここでも、引数の間に文字を持つテキスト行はありません。
呼び出されたコマンドに関する限り、シェルまたは他の親/前駆体プロセスで行われていることはプライベートで非表示です-標準Cがmain()
が受け入れることができると指定している文字列の配列のみが表示されます。
一般的には違います。コマンドラインの解析は、呼び出されたプログラムで解析されていない行を利用できないようにするシェルによって行われます。実際、プログラムは、文字列を解析するのではなく、プログラムで引数の配列を構築することによってargvを作成した別のプログラムから実行される可能性があります。
スペースがの一部引数でない限り、これは不可能です。
コマンドは配列から個々の引数にアクセスし(プログラミング言語に応じて1つの形式または別の形式で)、実際のコマンドラインは履歴ファイルに保存される場合があります(履歴ファイルがあるシェルのインタラクティブプロンプトで入力された場合)。いかなる形式でもコマンドに渡されることはありません。
Unix上のすべてのコマンドは、最終的にはexec()
ファミリーの関数の1つによって実行されます。これらはコマンド名と引数のリストまたは配列を取ります。シェルプロンプトで入力されたコマンドラインを使用するものはありません。 system()
関数は実行しますが、その文字列引数はexecve()
によって後で実行されます。これも、コマンドライン文字列ではなく引数の配列を取ります。
いつでもシェルに、どのシェルコードが実行につながるかをアプリケーションに伝えるように指示できます。たとえば、zsh
では、preexec()
フックを使用して_$Shell_CODE
_環境変数にその情報を渡すことで、例として使用されます(printenv
を使用すると、 getenv("Shell_CODE")
(プログラム内):
_$ preexec() export Shell_CODE=$1
$ printenv Shell_CODE
printenv Shell_CODE
$ printenv Shell_CODE
printenv CODE
$ $(echo printenv Shell_CODE)
$(echo printenv Shell_CODE)
$ for i in Shell_CODE; do printenv "$i"; done
for i in Shell_CODE; do printenv "$i"; done
$ printenv Shell_CODE; : other command
printenv Shell_CODE; : other command
$ f() printenv Shell_CODE
$ f
f
_
これらはすべてprintenv
を次のように実行します。
_execve("/usr/bin/printenv", ["printenv", "Shell_CODE"],
["PATH=...", ..., "Shell_CODE=..."]);
_
printenv
がこれらの引数を使用してprintenv
を実行するzshコードを取得できるようにします。あなたがその情報で何をしたいのか、私にはわかりません。
bash
を使用すると、zsh
のpreexec()
に最も近い機能が_$BASH_COMMAND
_をDEBUG
トラップで使用しますが、bash
は、ある程度の書き換えを行い(特に、区切り文字として使用される空白の一部をリファクタリングし)、プロンプトで入力されたコマンドライン全体ではなく、すべての(実行された)コマンド実行に適用されます(「 functrace
オプション)。
_$ trap 'export Shell_CODE="$BASH_COMMAND"' DEBUG
$ printenv Shell_CODE
printenv Shell_CODE
$ printenv $(echo 'Shell_CODE')
printenv $(echo 'Shell_CODE')
$ for i in Shell_CODE; do printenv "$i"; done; : other command
printenv "$i"
$ printf '%s\n' "$(printenv "Shell_CODE")"
printf '%s\n' "$(printenv "Shell_CODE")"
$ set -o functrace
$ printf '%s\n' "$(printenv "Shell_CODE")"
printenv "Shell_CODE"
$ print${-+env } $(echo 'Shell_CODE')
print${-+env } $(echo 'Shell_CODE')
_
シェル言語構文で区切り文字であるスペースの一部が1に圧縮されていること、およびコマンドライン全体が常にコマンドに渡されていないことを確認してください。したがって、おそらくあなたのケースでは役に立たないでしょう。
次のようにすべてのコマンドに機密情報が漏洩する可能性があるため、このようなことはお勧めしません。
_echo very_secret | wc -c | untrustedcmd
_
その秘密をwc
とuntrustedcmd
の両方に漏らします。
もちろん、シェル以外の言語でも同じようなことができます。たとえば、Cでは、コマンドを実行するCコードを環境にエクスポートするいくつかのマクロを使用できます。
_#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#define WRAP(x) (setenv("C_CODE", #x, 1), x)
int main(int argc, char *argv[])
{
if (!fork()) WRAP(execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (!fork()) WRAP(0 + execlp("printenv", "printenv", "C_CODE", NULL));
wait(NULL);
if (argc > 1 && !fork()) WRAP(execvp(argv[1], &argv[1]));
wait(NULL);
return 0;
}
_
例:
_$ ./a.out printenv C_CODE
execlp("printenv", "printenv", "C_CODE", NULL)
0 + execlp("printenv", "printenv", "C_CODE", NULL)
execvp(argv[1], &argv[1])
_
Bashの場合のように、いくつかのスペースがCプリプロセッサーによってどのように圧縮されたかを確認してください。すべてではないにしてもほとんどの言語では、区切り文字で使用されるスペースの量に違いはありません。そのため、コンパイラー/インタープリターがそれらをある程度自由に使用できることは当然のことです。
他の答えに欠けているものを追加します。
他の回答を見る
プログラムで実行できることは何もありませんが、プログラムを実行すると、シェルで実行できることがいくつかあります。
引用符を使用する必要があります。だから代わりに
./myprog aaa bbb
これらのいずれかを行う必要があります
./myprog " aaa bbb"
./myprog ' aaa bbb'
これは、すべてのスペースを含む単一の引数をプログラムに渡します。 2つには違いがあります。2番目はリテラルで、表示されるとおりの文字列です(ただし、'
は\'
と入力する必要があります)。最初のものはいくつかの文字を解釈しますが、いくつかの引数に分割されます。詳細については、シェルの引用を参照してください。したがって、シェルを書き直す必要はありません。シェルの設計者はすでにそのことを考えています。ただし、これは現在1つの引数なので、プログラム内でより多くの受け渡しを行う必要があります。
Stdinを介してデータを渡します。これは、大量のデータをコマンドに取り込む通常の方法です。例えば.
./myprog << EOF
aaa bbb
EOF
または
./myprog
Tell me what you want to tell me:
aaaa bbb
ctrl-d
(斜体はプログラムの出力です)