sudo catが許可を拒否したのに、sudo vimが正常に機能するのはなぜですか?


86

archのpacman.confファイルへのリポジトリソースの追加を自動化しようとしていますがecho、シェルスクリプトでコマンドを使用しています。ただし、次のように失敗します。-

sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf

-bash: /etc/pacman.conf: Permission denied

/etc/pacman.confにvimを使用して手動で変更を加えた場合、

sudo vim /etc/pacman.conf

でvimを終了すると:wq、すべてが正常に機能し、「許可が拒否されました」という苦情なしにpacman.confが手動で更新されました。

なぜそうなのですか?そして、どうすればsudo echo仕事に取り掛かることができますか?(ところで、私sudo catも使ってみましたが、許可が拒否されて失敗しました)


回答:


51

問題は、リダイレクトがによってではなく、元のシェルによって処理されていることですsudo。シェルは心を読むことができず、その特定>>sudoそれのためではなく、のためのものであることを知りません。

必要がある:

  1. リダイレクトを引用します(したがって、リダイレクトはに渡されます sudo)
  2. およびを使用しますsudo -s(つまりsudo、シェルを使用して引用符で囲まれたリダイレクトを処理します)。

1
したがって、このように2つのステップで実行します(1)sudo -s(2)echo "#test" >> / etc / pacman.confは機能します。しかし、これを1行で実行することは可能ですか?
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'私があなたに伝えようとしていたことです。
geekosaur 2012

2
実際、私は上記のあなたの答えを読んだ後sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directoryにそれを試しましたが、次のような奇妙なエラーが発生しました:-それが私がその後2ステップの手動プロセスを試した理由です。
Calvin Cheng

16
ああ.....このように最後の試みが働いていた-echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
カルバン・チェン

1
sudo構成の無効化についてどのように/どこで確認できます-sか?visudo?
Calvin Cheng

127

@geekosaurが説明したように、シェルはコマンドを実行する前にリダイレクトを実行します。これを入力すると:

sudo foo >/some/file

現在のシェルプロセスはそれ自体のコピーを作成し、最初/some/fileに書き込み用に開こうとし、次にそのファイル記述子を標準出力にしてから、を実行しsudoます。

許可されている場合(sudoer構成では、シェルの実行が妨げられることがよくあります)、次のようなことができます。

sudo bash -c 'foo >/some/file'

しかし、一般的に良い解決策は、の| sudo tee代わりに>との| sudo tee -a代わりに使用することです>>。そもそもリダイレクトが必要な唯一の理由である場合、これは特に便利ですsudo。結局のところ、rootとしてプロセスを不必要に実行することsudoは、避けるために作成されたものです。そしてecho、ルートとして実行するのはばかげています。

echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null

名前付きファイル独自の標準出力の両方に出力を送信する> /dev/nullため、最後に追加しました。端末で表示する必要はありません。(コマンドは。それは、その名前を取得する場所である、物理的なパイプラインの「T」コネクタのように働く)そして、私は(単一引用符に切り替え... (代わりにdoubleの)...そのすべてがリテラルおよびIであるので) inの前にバックスラッシュを置く必要はありませんでした。(引用符または円記号がないと、シェルパラメーターの値に置き換えられます。これはおそらく存在しません。この場合、は何にも置き換えられず、消えてしまいます。)teetee''""$$arch$archarch$arch

つまり、sudo。を使用してrootとしてファイルに書き込むことができます。次に、シェルスクリプトで改行を含むテキストを出力する方法についての長い余談があります。:)

彼らが言うように、それをBLUFするために、私の好ましい解決策は、ヒアドキュメントを上記のsudo teeコマンドにフィードすることです。その場合、catorechoまたはprintforまたはその他のコマンドはまったく必要ありません。一重引用符はセンチネルの紹介に移動しました<<'EOF'が、同じ効果があります。本文はリテラルテキストとして扱われるため、その$archままにしておきます。

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

しかし、それは私がそれを行う方法ですが、代替手段があります。ここにいくつかあります:

echo1行に1つずつ固定できますが、すべてをサブシェルにグループ化するため、ファイルに追加する必要があるのは1回だけです。

(echo '[archlinuxfr]'
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

(に追加-eし、echoその非POSIX拡張機能をサポートするシェルを使用している場合)\n:を使用して、文字列に改行を直接埋め込むことができます。

# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

しかし、上記のように、これはPOSIXで指定された動作ではありません。シェルは、代わり-eにリテラル\nの後にリテラルの束を含む文字列をエコーするだけかもしれません。これを行うPOSIXの方法はprintfecho;の代わりに使用することです。引数は自動的に処理されecho -eますが、末尾に改行が自動的に追加されないため、\nそこに余分な行を追加する必要があります。

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' | 
  sudo tee -a /etc/pacman.conf >/dev/null

これらの解決策のいずれかを使用すると、コマンドが引数文字列として取得するものには2文字のシーケンスが含まれ、\nそれを改行に変換するのはコマンドプログラム自体(printfまたは内のコードecho)次第です。最近の多くのシェルでは、ANSI引用符$'...を使用するオプションがあります。これにより、コマンドプログラムが文字列を認識する前に、の'ようなシーケンス\nリテラル改行に変換されます。つまり、このような文字列は、プレーン-eオールドレスを含むすべてのコマンドで機能しますecho

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

ただし、echo -eANSI引用符はより移植性がありますが、それでもPOSIX以外の拡張機能です。

繰り返しになりますが、これらはすべてオプションですが、私はtee <<EOF上記のストレートソリューションを好みます。


面白い!ファイルを/ dev / nullにチャンクする理由を説明するメモを追加できますか?
knite 2014

sudo bash -c 'foo >/some/file'osxで私のために働きます:
kayochin

複数行のテキストで機能するため、この回答が好きです。cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

あなたが無関係なcatプロセスを追加したことを除いて、それは事実上私の最後の答えです。:)
マークリード

17

http://www.innovationsts.com/blog/?p=2758

上記の手順はそれほど明確ではないため、私はそのブログ投稿の手順を使用しています。例を使用すると、何をする必要があるかが簡単にわかります。

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash:/root/example.gz:アクセスが拒否されました

エラーの原因はパイプラインの2番目のコマンド(gzipコマンド)であることに注意してください。そこで、-cオプションを指定してbashを使用する手法が登場します。

$ sudo bash -c'cat /root/example.txt | gzip> /root/example.gz'$
sudo ls /root/example.gz
/root/example.gz

lsコマンドの出力から、圧縮ファイルの作成が成功したことがわかります。

2番目の方法は、コマンド文字列をbashに渡すという点で最初の方法と似ていますが、sudoを介してパイプラインで実行しています。

$ sudo rm /root/example.gz
$ echo "cat /root/example.txt | gzip> /root/example.gz" | sudo bash
$ sudo ls /root/example.gz
/root/example.gz


1
このような単純なコマンドの場合、bashの代わりにshを使用する方が少し良いかもしれません。例:sudo sh -c'cat /root/example.txt | gzip> /root/example.gz '。しかし、そうでなければ、良い説明、+ 1。
ブライス2012


1

ステップ1bashファイルに関数を作成します(write_pacman.sh

#!/bin/bash

function write_pacman {
 tee -a /etc/pacman.conf > /dev/null << 'EOF'
  [archlinuxfr]
  Server = http://repo.archlinux.fr/\$arch
EOF
}

'EOF'$arch変数を解釈しません。

STE2ソースbashファイル

$ source write_pacman.sh

ステップ3機能を実行する

$ write_pacman

EOFの見積もりは何ですか?
bilabila 2018

0

ファイルの追加(sudo cat):

cat <origin-file> | sudo tee -a <target-file>

ファイルにエコーを追加する(sudoエコー):

echo <origin> | sudo tee -a <target-file>

(追加)出力を無視します:

echo >origin> | sudo tee -a <target-file> >/dev/null
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.