Tại sao sudo cat cung cấp Quyền bị từ chối nhưng sudo vim hoạt động tốt?


86

Tôi đang cố gắng tự động hóa việc bổ sung nguồn kho lưu trữ trong tệp pacman.conf của vòm nhưng sử dụng echolệnh trong tập lệnh shell của tôi. Tuy nhiên, nó không thành công như thế này: -

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

Nếu tôi thực hiện thay đổi đối với /etc/pacman.conf theo cách thủ công bằng vim, bằng cách

sudo vim /etc/pacman.conf

và rung cảm với :wq, mọi thứ hoạt động tốt và pacman.conf của tôi đã được cập nhật theo cách thủ công mà không có khiếu nại "Quyền bị từ chối".

Tại sao cái này rất? Và làm thế nào để tôi đi sudo echolàm? (btw, tôi cũng đã thử sử dụng sudo catnhưng không thành công với Quyền bị từ chối)


Câu trả lời:


51

Vấn đề là sự chuyển hướng đang được xử lý bởi shell ban đầu của bạn, không phải bởi sudo. Vỏ không có khả năng đọc suy nghĩ và không biết rằng đặc biệt đó >>là dành cho sudovà không dành cho nó.

Bạn cần phải:

  1. trích dẫn chuyển hướng (vì vậy nó được chuyển cho sudo)
  2. sử dụng sudo -s(để sudosử dụng một trình bao để xử lý chuyển hướng được trích dẫn.)

1
Vì vậy, thực hiện theo hai bước như sau (1) sudo -s(2) echo "# test" >> /etc/pacman.conf hoạt động. Nhưng có thể thực hiện điều này trong một dòng duy nhất không?
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'là những gì tôi đang cố gắng truyền đạt cho bạn.
geekosaur

2
Trên thực tế, tôi đã thử điều đó sau khi đọc câu trả lời của bạn ở trên nhưng gặp lỗi kỳ lạ như sau: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directoryđó là lý do tại sao sau đó tôi đã thử quy trình thủ công 2 bước.
Calvin Cheng

16
Ah ..... một nỗ lực cuối cùng theo cách này đã thành công -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng

1
Làm thế nào / ở đâu để tôi tìm hiểu về sudoviệc vô hiệu hóa cấu hình -s? visudo?
Calvin Cheng

127

Như @geekosaur đã giải thích, shell thực hiện chuyển hướng trước khi chạy lệnh. Khi bạn gõ cái này:

sudo foo >/some/file

Quy trình shell hiện tại của bạn tạo một bản sao của chính nó mà trước tiên cố gắng mở /some/fileđể ghi, sau đó làm cho bộ mô tả tệp đó đầu ra tiêu chuẩn của nó và chỉ sau đó thực thi sudo.

Nếu được phép (cấu hình sudoer thường loại trừ các trình bao đang chạy), bạn có thể làm như sau:

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

Nhưng tôi thấy một giải pháp tốt nói chung là sử dụng | sudo teethay vì >| sudo tee -athay vì >>. Điều đó đặc biệt hữu ích nếu chuyển hướng là lý do duy nhất tôi cần sudongay từ đầu; xét cho cùng, việc chạy các tiến trình không cần thiết với tư cách là người chủ chính là điều sudocần tránh. Và chạy echonhư root chỉ là ngớ ngẩn.

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

Tôi đã thêm > /dev/nullvào cuối vì teegửi đầu ra của nó đến cả tệp được đặt tên đầu ra tiêu chuẩn của riêng nó, và tôi không cần phải xem nó trên thiết bị đầu cuối của mình. ( teeLệnh hoạt động giống như một trình kết nối "T" trong một đường ống vật lý, đó là nơi nó được đặt tên.) Và tôi đã chuyển sang dấu nháy đơn ( '... ') thay vì dấu ngoặc kép ( "... ") để mọi thứ đều theo nghĩa đen và tôi không cần phải đặt một dấu gạch chéo ngược ở phía trước của $trong $arch. (Nếu không có dấu ngoặc kép hoặc dấu gạch chéo ngược, $archsẽ được thay thế bằng giá trị của tham số shell arch, có thể không tồn tại, trong trường hợp đó, tham số $archnày được thay thế bằng không và chỉ biến mất.)

Vì vậy, điều đó sẽ xử lý việc ghi vào tệp dưới dạng root bằng cách sử dụng sudo. Bây giờ là một phép phân tích dài dòng về các cách xuất văn bản chứa dòng mới trong một tập lệnh shell. :)

Đối với BLUF, như họ nói, giải pháp ưa thích của tôi là chỉ nạp một tài liệu tại đây vào sudo teelệnh trên ; thì không có nhu cầu cathoặc echohoặc printfhoặc bất kỳ lệnh khác ở tất cả. Các dấu ngoặc kép đơn đã được chuyển đến phần giới thiệu sentinel <<'EOF', nhưng chúng có cùng tác dụng ở đó: phần thân được coi như văn bản theo nghĩa đen, do đó, $archchỉ còn lại

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

EOF

Nhưng trong khi đó là cách tôi làm, vẫn có những lựa chọn thay thế. Ở đây có một ít:

Bạn có thể gắn một echotệp trên mỗi dòng, nhưng nhóm tất cả chúng lại với nhau trong một vỏ con, vì vậy bạn chỉ phải thêm vào tệp một lần:

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

Nếu bạn thêm -evào echo(và bạn đang sử dụng một trình bao hỗ trợ phần mở rộng không phải POSIX đó), bạn có thể nhúng trực tiếp dòng mới vào chuỗi bằng cách sử dụng \n:

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

Nhưng như đã nói ở trên, đó không phải là hành vi được chỉ định bởi POSIX; shell của bạn có thể chỉ lặp lại một ký tự -etheo sau bởi một chuỗi với một loạt các ký tự \nthay thế. Cách POSIX để làm điều đó là sử dụng printfthay vì echo; nó tự động xử lý đối số của nó như echo -ehiện tại, nhưng không tự động thêm một dòng mới vào cuối, vì vậy bạn cũng phải gắn thêm một dòng ở \nđó:

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

Với một trong hai giải pháp đó, những gì lệnh nhận được dưới dạng chuỗi đối số chứa chuỗi hai ký tự \nvà chương trình lệnh (mã bên trong printfhoặc echo) có thể dịch nó thành một dòng mới. Trong nhiều vỏ hiện đại, bạn có tùy chọn của việc sử dụng dấu ngoặc kép ANSI $'... ', mà sẽ dịch các chuỗi như \nthành đen dòng mới trước khi chương trình lệnh bao giờ nhìn thấy chuỗi. Điều đó có nghĩa là các chuỗi như vậy hoạt động với bất kỳ lệnh nào, kể cả lệnh old -e-less đơn giản echo:

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

Tuy nhiên, mặc dù linh hoạt hơn echo -e, nhưng dấu ngoặc kép ANSI vẫn là một phần mở rộng không phải POSIX.

Và một lần nữa, trong khi đó là tất cả các lựa chọn, tôi thích tee <<EOFgiải pháp đơn giản ở trên.


Hấp dẫn! Bạn có thể thêm ghi chú giải thích lý do cũng phân đoạn tệp thành / dev / null không?
tweak

sudo bash -c 'foo >/some/file'làm việc cho tôi trên osx :-)
kayochin

Tôi thích câu trả lời này hơn vì nó hoạt động với văn bản nhiều dòng. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

Đó thực sự là câu trả lời cuối cùng của tôi, ngoại trừ việc bạn đã thêm một catquy trình không liên quan . :)
Mark Reed

17

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

Vì các hướng dẫn không rõ ràng ở trên, tôi đang sử dụng các hướng dẫn từ bài đăng blog đó. Với các ví dụ để bạn dễ dàng xem những gì bạn cần làm.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Quyền bị từ chối

Lưu ý rằng đó là lệnh thứ hai (lệnh gzip) trong đường dẫn gây ra lỗi. Đó là nơi kỹ thuật sử dụng bash với tùy chọn -c của chúng tôi xuất hiện.

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

Chúng ta có thể thấy đầu ra của lệnh ls mà việc tạo tệp nén đã thành công.

Phương pháp thứ hai tương tự như phương pháp đầu tiên ở chỗ chúng ta đang truyền một chuỗi lệnh để bash, nhưng chúng ta đang thực hiện nó trong một đường ống thông qua 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
Đối với một lệnh đơn giản như vậy, có thể tốt hơn một chút nếu sử dụng sh thay vì bash. Ví dụ: sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Nhưng nếu không, những lời giải thích tốt, +1.
bryce


1

BƯỚC 1 tạo một hàm trong tệp 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'sẽ không diễn giải $archbiến.

Tệp bash nguồn STE2

$ source write_pacman.sh

BƯỚC 3 thực hiện chức năng

$ write_pacman

báo giá trên EOF để làm gì?
bilabila

0

chắp thêm tệp (mèo sudo):

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

nối tiếng vọng vào tệp (sudo echo):

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

(EXTRA) bỏ qua phần bổ sung:

echo >origin> | sudo tee -a <target-file> >/dev/null
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.