web-dev-qa-db-ja.com

Sudo catが許可を拒否するのにSudo vimは正常に動作するのはなぜですか?

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

Vimを使用して/etc/pacman.confを手動で変更した場合、

Sudo vim /etc/pacman.conf

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

これはなぜですか? Sudo echoを機能させるにはどうすればよいですか? (ところで、私はSudo catも使用しようとしましたが、アクセス許可も拒否されて失敗しました)

75
Calvin Cheng

問題は、リダイレクトがSudoではなく、元のシェルによって処理されていることです。シェルは心を読み取ることができず、特定の>>Sudoを対象としており、それを目的としていないことを知りません。

必要がある:

  1. リダイレクションを引用します(したがって、Sudo)に渡されます
  2. および use Sudo -s(したがって、Sudoはシェルを使用して引用されたリダイレクトを処理します。)
42
geekosaur

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

Sudo foo >/some/file

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

許可されている場合(sudoerの設定により、実行中のシェルが除外されることがよくあります)、次のようなことができます。

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

しかし、一般的に良い解決策は、| Sudo teeの代わりに>| Sudo tee -aの代わりに>>を使用することです。これは、リダイレクトが最初にSudoを必要とする唯一の理由である場合に特に役立ちます。結局、rootとして不必要に実行されるプロセスは、まさに回避するために作成されたSudoです。そして、echoをrootとして実行するのは馬鹿げています。

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を追加しました。teeがその出力をboth名前付きファイルに送信しますand独自の標準出力であり、私の端末で見る必要があります。 (teeコマンドは、その名前を取得する物理パイプラインの「T」コネクタのように機能します。)そして、単一引用符に切り替えました('...' )ダブル("...")の代わりに、すべてがリテラルであり、$$Archの前にバックスラッシュを置く必要はありませんでした。

そのため、Sudoを使用して、rootとしてファイルに書き込みます。さて、改行を含むテキストをシェルスクリプトで出力する方法についての長い余談です。 :)

最初に、すべてのechoをサブシェルにグループ化するだけでよいので、リダイレクトを1回行うだけで済みます。

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

または、printfの代わりにechoを使用して、\nを使用して文字列に改行を直接埋め込むことができます。

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

bashでは、echo -eで同じ結果を得ることができます:

# BASH ONLY - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$Arch\n ' | 
  Sudo tee -a /etc/pacman.conf >/dev/null

ただし、ほとんどのシェルは、試してみると-eを出力するだけなので、お勧めしません。

printfecho -eの両方を使用すると、コマンドが引数文字列として取得するものには、\nと入力する場所にリテラルバックスラッシュとそれに続くリテラルNが含まれ、コマンドプログラム次第です( printfまたはecho)内のコードは、それを改行に変換します。最近の多くのシェルでは、ANSI引用符$'...'を使用するオプションがあります。これは、コマンドの前に\nなどのシーケンスをliteral改行に変換しますプログラムは文字列を見ます。つまり、そのような文字列はどのコマンドでも動作します。

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

しかし、echo -eより移植性が高い一方で、ANSI引用符は依然として非POSIX拡張機能です。

これを行うための好ましい方法は、ヒアドキュメントを使用し、echoまたはprintfの必要性を完全に回避することです。

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

EOF
113
Mark Reed

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

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

$ Sudo cat /root/example.txt | gzip> /root/example.gz
-bash:/root/example.gz:許可が拒否されました

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

$ 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 ls /root/example.gz
/root/example.gz

17
nelaaro
Sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
6
Peyman Mahdian

ステップ1 bashファイルに関数を作成します(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

ステップ関数を実行する

$ write_pacman
1
prayagupd