完全な標準入力を変数として読みたいです。私は新しいプロセスを作成したくありません。
Bashのマニュアルから:
コマンド置換を使用すると、
$(cat file)
コマンドを同じですがより高速なコマンドに置き換えることができます$(< file)
。
デフォルトでは機能しますが、/proc/fd
問題は次のように簡単に壊れますsudo
。
# same user. works
[root@okdistr ~]# echo aaa | bash -c 'echo $(</dev/stdin)'
aaa
# different user. fail
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(</dev/stdin)'
bash: /dev/stdin: Permission denied
# spawn new process. not want
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(cat)'
aaa
# try to read from fd: silently fails
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
# works, but too complex
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'a=; while true; do rc=0; read -N1024 b || rc=$?; a=$a$b; [ $rc = 0 ] || break; done; echo "$a"'
aaa
[root@okdistr ~]#
なぜ失敗したのです$(<&0)
か?
答え1
まず、私が提案する解決策は次のとおりです。次に、観察した2つのエラーについて説明します。
提案されたソリューション
Bash変数にはNUL文字を含めることはできません。したがって、ファイルに対応する文字が含まれていない場合にのみ、ファイル全体を bash 変数として読み取ることができます。このbashの制限に応じて、以下が適切です。
$ echo aaa | { read -rd "" v; echo "$v"; }
aaa
を使用すると、read -d '' var
bashはNUL文字が見つかるまで標準入力を読み取ります。 bash変数にはとにかくNUL文字を含めることはできないため、このアプローチはbashの固有の制限を超えてユーザーを制限しません。
bashがバックスラッシュシーケンスを解釈するのを防ぐオプションです-r
。read
また、先頭と末尾のスペースを維持するには、ステートメントの前にスペースを追加しIFS=
てくださいread
。
"fdから読み取ろうとしました:自動的に失敗しました。"
# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
#
上記の自動障害はsudoなしで発生します。
$ echo aaa | bash -c 'echo $(<&0)'
$
サブシェルを作成しなくても、これが発生します。
$ echo aaa | echo $(<&0)
$
問題は、これが&0
有効なファイル名ではないことです。観察する:
$ echo aaa | cat &0
[1] 22635
bash: 0: command not found
Bashでは、andまたは&0
と<
組み合わせるときにのみ意味があります>
。
bashのドキュメントをもう一度見てみましょう。
コマンド置換 $(cat file) は同じですが、より高速な $(< file) で置き換えることができます。
cat &0
うまくいかないので期待し$(< &0)
てはいけません。
「他のユーザーです。失敗しました。」
これは合理的なことのようです。
# echo aaa | sudo -u nobody bash -c 'echo $(cat /dev/stdin)'
cat: /dev/stdin: Permission denied
失敗の理由を理解するために、次の権限を確認してみましょう/dev/stdin
。
# echo aaa | sudo -u nobody bash -c 'ls -lH /dev/stdin'
prw------- 1 root root 0 Jul 1 15:42 /dev/stdin
ユーザーにファイルにアクセスする権限がありません。これは意味があります:誰かがルートのファイルを台無しにしたくありません。
「よりスマートな」オペレーティングシステムには誰もアクセスできないことがわかります。/dev/stdin
この特別なケースではしかし、セキュリティのためには、オペレーティングシステムが自分自身を上回ろうとしないことが最善です。
答え2
これを行うには、これらのリンクは必要ありません/dev
。環境変数が消去されたことはわかっていますが、sudo
とにかく別のシェルを呼び出すので、変数を引数として渡すことができます。
sudo -u nobody bash -c '
a=$1; echo "$a"
' -- "$(echo aaa)"
...または...
echo aaa | {
sudo -u nobody bash -c '
a=$1; echo "$a"
' -- "$(</dev/fd/0)"
}
これら2つは次のように印刷されます。
aaa
...標準出力へ...