Mengapa sudo cat memberikan Izin ditolak tetapi sudo vim berfungsi dengan baik?

87

Saya mencoba untuk mengotomatiskan penambahan sumber repositori di file pacman.conf arch saya tetapi menggunakan echoperintah di skrip shell saya. Namun, gagal seperti ini: -

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

Jika saya mengubah /etc/pacman.conf secara manual menggunakan vim, dengan melakukan

sudo vim /etc/pacman.conf

dan berhenti menggunakan vim :wq, semuanya berfungsi dengan baik dan pacman.conf saya telah diperbarui secara manual tanpa keluhan "Izin ditolak".

Mengapa demikian? Dan bagaimana cara saya sudo echobekerja? (btw, saya mencoba menggunakan sudo catjuga tetapi gagal dengan Izin ditolak juga)

Calvin Cheng
sumber
3
kemungkinan duplikat dari Bagaimana cara menggunakan sudo untuk mengarahkan keluaran ke lokasi yang saya tidak memiliki izin untuk menulis?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

52

Masalahnya adalah bahwa pengalihan sedang diproses oleh shell asli Anda, bukan oleh sudo. Kerang tidak mampu membaca pikiran dan tidak tahu bahwa hal khusus >>itu dimaksudkan untuk itu sudodan bukan untuk itu.

Kamu butuh:

  1. mengutip pengalihan (sehingga diteruskan ke sudo)
  2. dan gunakan sudo -s(sehingga sudomenggunakan shell untuk memproses pengalihan yang dikutip.)
geekosaurus
sumber
1
Jadi melakukannya dalam dua langkah seperti ini (1) sudo -s(2) echo "# test" >> /etc/pacman.conf bekerja. Tetapi apakah mungkin untuk mengeksekusi ini dalam satu baris?
Calvin Cheng
15
sudo -s 'echo "# test" >>/etc/pacman.conf'adalah apa yang saya coba sampaikan kepada Anda.
geekosaur
2
Sebenarnya, saya mencobanya setelah membaca jawaban Anda di atas tetapi mendapat kesalahan aneh seperti ini: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directoryitulah sebabnya saya kemudian mencoba proses manual 2 langkah.
Calvin Cheng
16
Ah ..... satu usaha terakhir dengan cara ini berhasil -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng
1
bagaimana / di mana saya mengetahui tentang sudopenonaktifan konfigurasi -s? Visudo?
Calvin Cheng
129

Seperti yang dijelaskan @geekosaur, shell melakukan pengalihan sebelum menjalankan perintah. Saat Anda mengetik ini:

sudo foo >/some/file

Proses shell Anda saat ini membuat salinannya sendiri yang pertama kali mencoba dibuka /some/fileuntuk penulisan, kemudian jika berhasil, deskriptor file tersebut akan menjadi keluaran standarnya, dan hanya jika berhasil, ia akan dieksekusi sudo. Ini gagal pada langkah pertama.

Jika Anda diizinkan (konfigurasi sudoer sering menghalangi menjalankan shell), Anda dapat melakukan sesuatu seperti ini:

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

Tapi saya menemukan solusi yang baik secara umum adalah dengan menggunakan | sudo teebukannya >dan | sudo tee -abukan >>. Itu sangat berguna jika pengalihan adalah satu-satunya alasan yang saya perlukan sudosejak awal; lagipula, menjalankan proses yang tidak perlu sebagai root adalah hal yang sudosebenarnya harus dihindari. Dan menjalankan echosebagai root itu konyol.

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

Saya menambahkan > /dev/nulldi akhir karena teemengirimkan output ke kedua file bernama dan output standar sendiri, dan saya tidak perlu melihatnya di terminal saya. ( teePerintah tersebut bertindak seperti konektor "T" dalam pipa fisik, yang merupakan asal namanya.) Dan saya beralih ke tanda kutip tunggal ( '... ') daripada tanda kutip ganda ( "... ") sehingga semuanya literal dan saya tidak perlu membuat garis miring terbalik di depan $dalam $arch. (Tanpa tanda kutip atau garis miring terbalik, $archakan digantikan oleh nilai parameter shell arch, yang mungkin tidak ada, dalam hal $archini diganti dengan tidak ada dan menghilang begitu saja.)

Sehingga menangani penulisan ke file sebagai root menggunakan sudo. Sekarang untuk penyimpangan panjang tentang cara menghasilkan teks yang mengandung baris baru dalam skrip shell. :)

Untuk BLUF, seperti yang mereka katakan, solusi pilihan saya adalah dengan memasukkan dokumen di sini ke dalam sudo teeperintah di atas ; maka tidak diperlukan catatau echoatau printfatau perintah lain sama sekali. Tanda kutip tunggal telah dipindahkan ke pengantar sentinel <<'EOF', tetapi memiliki efek yang sama di sana: isi diperlakukan sebagai teks literal, jadi $archdibiarkan sendiri:

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

EOF

Tapi meski begitulah cara saya melakukannya, ada alternatif. Berikut ini beberapa di antaranya:

Anda dapat tetap menggunakan satu echoper baris, tetapi kelompokkan semuanya menjadi subkulit, jadi Anda hanya perlu menambahkan ke file satu kali:

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

Jika Anda menambahkan -eke echo(dan Anda menggunakan shell yang mendukung ekstensi non-POSIX), Anda dapat menyematkan baris baru langsung ke dalam string menggunakan \n:

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

Tetapi seperti yang dikatakan di atas, itu bukanlah perilaku yang ditentukan POSIX; shell Anda mungkin hanya menggemakan literal -ediikuti dengan string dengan sekelompok literal \nsebagai gantinya. Cara POSIX untuk melakukannya adalah dengan menggunakan printfalih-alih echo; itu secara otomatis memperlakukan argumennya seperti echo -ehalnya, tetapi tidak secara otomatis menambahkan baris baru di akhir, jadi Anda harus menempelkan tambahan di \nsana juga:

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

Dengan salah satu solusi tersebut, apa yang didapat perintah sebagai string argumen berisi urutan dua karakter \n, dan terserah pada program perintah itu sendiri (kode di dalam printfatau echo) untuk menerjemahkannya ke dalam baris baru. Di banyak shell modern, Anda memiliki opsi untuk menggunakan tanda kutip ANSI $'... ', yang akan menerjemahkan urutan seperti \nke dalam baris baru literal sebelum program perintah melihat string tersebut. Itu berarti string seperti bekerja dengan perintah apapun, termasuk tua polos -e-kurang echo:

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

Namun, meskipun lebih portabel daripada echo -e, kutipan ANSI masih merupakan ekstensi non-POSIX.

Dan lagi, sementara itu semua adalah opsi, saya lebih suka tee <<EOFsolusi langsung di atas.

Mark Reed
sumber
Menarik! Bisakah Anda menambahkan catatan yang menjelaskan alasan juga membagi file ke / dev / null?
rajutan
sudo bash -c 'foo >/some/file'bekerja untuk saya di osx :-)
kayochin
Saya lebih suka jawaban ini karena berfungsi dengan teks multiline. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan
Itu adalah jawaban terakhir saya yang efektif, kecuali Anda telah menambahkan catproses yang tidak relevan . :)
Mark Reed
17

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

Karena petunjuknya tidak begitu jelas di atas, saya menggunakan petunjuk dari posting blog itu. Dengan contoh sehingga lebih mudah untuk melihat apa yang perlu Anda lakukan.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Izin ditolak

Perhatikan bahwa perintah kedua (perintah gzip) di pipeline yang menyebabkan error. Di situlah teknik kami menggunakan bash dengan opsi -c masuk.

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

Kita dapat melihat dari output perintah ls bahwa pembuatan file terkompresi berhasil.

Metode kedua mirip dengan yang pertama karena kami mengirimkan string perintah ke bash, tetapi kami melakukannya dalam pipeline melalui 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

nelaaro
sumber
1
Untuk perintah sederhana seperti itu, mungkin lebih baik menggunakan sh daripada bash. Misalnya sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Tapi sebaliknya, penjelasannya bagus, +1.
bryce
7
sudo bash -c 'echo "[archlinuxfr]" >> /etc/pacman.conf'
Peyman Mahdian
sumber
1

LANGKAH 1 buat fungsi di file 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'tidak akan menafsirkan $archvariabel.

File bash sumber STE2

$ source write_pacman.sh

LANGKAH 3 jalankan fungsi

$ write_pacman
prayagupd.dll
sumber
untuk apa kutipan di EOF?
bilabila
0

tambahkan file (sudo cat):

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

tambahkan echo ke file (sudo echo):

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

(EXTRA) abaikan ouput:

echo >origin> | sudo tee -a <target-file> >/dev/null
diogowalterdefreitas
sumber