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