【Bash】リダイレクトやパイプの権限
経緯
bash で実行ユーザーが write 権限を持たないファイルに sudo cat コマンドで書き込もうとしてエラーにあたりました。リダイレクトとパイプについて少し知識が深まったのでメモを残します。
$ whoami
testuser
$ ls -l ./testfile
-rw------- 1 otheruser othergroup 13 Apr 7 20:23 ./testfile
$ sudo cat ./testfile
this is test
$ sudo cat << EOF > ./testfile
> abc
> EOF
-bash: ./testfile: Permission denied
$ sudo cat ./testfile
this is test
リダイレクトへの理解が浅く、 Permission denied されました。
ここでは cat は標準入力から入力を受け付けて標準出力に出力しているだけなので特権を必要としていません。必要としているのは他のユーザー otheruser が所有者になっているファイル ./testfile に書き込もうとしているリダイレクト>でした。
原因
リダイレクト > はコマンドではなくシェルの機能[1]なのでシェルの実行ユーザーの権限を持ちます。この権限が出力先ファイルの書き込み権限を満たさないといけません。パイプ | も同じです。
間違ったパターン
sudo cat パターン
上記経緯のパターン
$ whoami
testuser
$ ls -l ./testfile
-rw------- 1 otheruser othergroup 13 Apr 7 20:23 ./testfile
$ sudo cat ./testfile
this is test
$ sudo cat << EOF > ./testfile
> abc
> EOF
-bash: ./testfile: Permission denied
$ sudo cat ./testfile
this is test
cat が必要とする権限は入力となるファイルの read 権限だけなので、 cat に対して sudo は必要なく、ここで Permission denied されているのはリダイレクトです。つまり原因はリダイレクト (シェル) がファイルへの write 権限を持っていないことでした。
正解パターン
sudo sh -c パターン
sudo sh -c を使うパターン。 sh は bash でも大丈夫です[2]。
$ sudo sh -c "cat > ./testfile" << EOF
> abc
> EOF
$ sudo cat ./testfile
abc
sudo tee パターン
リダイレクトの代わりに パイプ -> sudo tee 経由で目的のファイルに渡すパターン。teeが sudo 権限を持つので ./testfile に書き込むことができます。tee から標準出力への出力は /dev/null に捨てます。
$ sudo tee ./testfile > /dev/null << EOF
> xyz
> EOF
$ sudo cat ./testfile
xyz
最後に
sudo sh -c パターンが一番わかりやすいと感じました。よりよい解決方法などがありましたらコメントで教えてもらえると嬉しいです。
参考
sudo(8) - Linux manual page
Redirections - Bash Reference Manual
LICENSE
本記事は CC-BY-SA-4.0 ライセンスの下に公開しています。
このライセンス文の日本語版は下記ページをご確認ください。

Discussion