ทำไม sudo cat ให้การอนุญาตถูกปฏิเสธ แต่ sudo vim ใช้งานได้ดี?


87

ฉันกำลังพยายามเพิ่มแหล่งที่เก็บโดยอัตโนมัติในไฟล์ pacman.conf ของ arch ของฉัน แต่ใช้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

และเลิกใช้งานเป็นกลุ่ม:wqทุกอย่างทำงานได้ดีและ pacman.conf ของฉันได้รับการอัปเดตด้วยตนเองโดยไม่มีข้อร้องเรียน "ปฏิเสธการอนุญาต"

ทำไมจึงเป็นเช่นนั้น? และฉันจะsudo echoไปทำงานได้อย่างไร? (btw ฉันลองใช้sudo catด้วย แต่ก็ล้มเหลวด้วยการปฏิเสธสิทธิ์เช่นกัน)


คำตอบ:


52

sudoปัญหาคือว่าการเปลี่ยนเส้นทางจะถูกประมวลผลโดยเปลือกเดิมของคุณไม่ได้โดย หอยไม่มีความสามารถในการอ่านใจและไม่รู้ว่าสิ่งนั้น>>มีไว้เพื่อสิ่งนั้นsudoและไม่ใช่เพื่อมัน

คุณต้อง:

  1. อ้างถึงการเปลี่ยนเส้นทาง (เพื่อส่งต่อไปยัง sudo)
  2. และใช้sudo -s(เพื่อที่sudoจะใช้เชลล์เพื่อประมวลผลการเปลี่ยนเส้นทางที่ยกมา)

1
ดังนั้นการทำในสองขั้นตอนเช่นนี้ (1) sudo -s(2) echo "# test" >> /etc/pacman.conf ได้ผล แต่เป็นไปได้ไหมที่จะดำเนินการในบรรทัดเดียว
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'คือสิ่งที่ฉันพยายามจะสื่อถึงคุณ
geekosaur

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
Calvin Cheng

1
ฉันจะทราบเกี่ยวกับsudoการปิดใช้งานการกำหนดค่าได้-sอย่างไร? วิสุโด?
Calvin Cheng

129

ตามที่ @geekosaur อธิบายเชลล์จะทำการเปลี่ยนเส้นทางก่อนที่จะรันคำสั่ง เมื่อคุณพิมพ์สิ่งนี้:

sudo foo >/some/file

กระบวนการเชลล์ปัจจุบันของคุณสร้างสำเนาของตัวเองที่พยายามเปิดขึ้น/some/fileเพื่อเขียนเป็นครั้งแรกจากนั้นหากประสบความสำเร็จจะทำให้ไฟล์นั้นเป็นตัวอธิบายเอาต์พุตมาตรฐานและเฉพาะในกรณีที่ประสบความสำเร็จเท่านั้นจึงจะดำเนินการsudoได้ นี่คือความล้มเหลวในขั้นตอนแรก

หากคุณได้รับอนุญาต (การกำหนดค่า sudoer มักจะไม่รวมเชลล์ที่กำลังรันอยู่) คุณสามารถทำสิ่งนี้ได้:

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

แต่ฉันคิดว่าเป็นทางออกที่ดีโดยทั่วไปคือการใช้| sudo teeแทน>และแทน| sudo tee -a >>นี่เป็นประโยชน์อย่างยิ่งหากการเปลี่ยนเส้นทางเป็นเหตุผลเดียวที่ฉันต้องการsudoตั้งแต่แรก ท้ายที่สุดกระบวนการทำงานโดยไม่จำเป็นเนื่องจากรูทเป็นสิ่งที่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ที่สิ้นสุดเพราะteeส่งออกของไปทั้งไฟล์ชื่อและออกมาตรฐานของตัวเองและฉันไม่ต้องการที่จะเห็นมันใน terminal ของฉัน ( teeคำสั่งทำหน้าที่เหมือนตัวเชื่อมต่อ "T" ในไปป์ไลน์ทางกายภาพซึ่งเป็นที่มาของชื่อ) และฉันเปลี่ยนไปใช้เครื่องหมายคำพูดเดี่ยว ( '... ') แทนที่จะเป็นคู่ ( "... ") เพื่อให้ทุกอย่างเป็นตัวอักษรและฉัน ไม่ต้องใส่เครื่องหมายในด้านหน้าของใน$ $arch(หากไม่มีเครื่องหมายคำพูดหรือแบ็กสแลช$archจะถูกแทนที่ด้วยค่าของพารามิเตอร์เชลล์archซึ่งอาจไม่มีอยู่จริงซึ่งในกรณีนี้$archจะถูกแทนที่ด้วยอะไรและหายไป)

ดังนั้นจะดูแลการเขียนไปยังไฟล์เป็นรูทโดยใช้sudo. ตอนนี้สำหรับการพูดนอกเรื่องที่มีความยาวเกี่ยวกับวิธีการส่งออกข้อความที่มีขึ้นบรรทัดใหม่ในเชลล์สคริปต์ :)

หากต้องการ BLUF อย่างที่พวกเขากล่าวทางออกที่ฉันต้องการคือเพียงแค่ป้อนเอกสารที่นี่ลงในsudo teeคำสั่งด้านบน จากนั้นไม่จำเป็นต้องมีcatหรือechoหรือprintfหรือคำสั่งอื่นใดเลย เครื่องหมายคำพูดเดี่ยวได้ย้ายไปที่คำนำของ Sentinel <<'EOF'แต่มีผลเหมือนกันที่นั่น: เนื้อหาจะถือว่าเป็นข้อความตามตัวอักษรดังนั้นจึง$archถูกทิ้งไว้ตามลำพัง:

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

EOF

แต่ในขณะที่ฉันทำมันก็มีทางเลือกอื่น นี่คือบางส่วน:

คุณสามารถใช้หนึ่งรายการechoต่อบรรทัด แต่จัดกลุ่มทั้งหมดเข้าด้วยกันใน subshell ดังนั้นคุณจะต้องต่อท้ายไฟล์เพียงครั้งเดียว:

(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 คือการใช้printfแทนecho; มันจะถือว่าอาร์กิวเมนต์ของมันโดยอัตโนมัติเช่นเดียวกับecho -eแต่จะไม่ต่อท้ายบรรทัดใหม่โดยอัตโนมัติดังนั้นคุณต้องติดเพิ่มที่\nนั่นด้วย:

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

ด้วยวิธีแก้ปัญหาเหล่านี้สิ่งที่คำสั่งได้รับเป็นสตริงอาร์กิวเมนต์ประกอบด้วยลำดับสองอักขระ\nและขึ้นอยู่กับโปรแกรมคำสั่งเอง (โค้ดภายในprintfหรือecho) เพื่อแปลสิ่งนั้นเป็นบรรทัดใหม่ ในเชลล์สมัยใหม่จำนวนมากคุณมีตัวเลือกในการใช้เครื่องหมายคำพูด ANSI $'... 'ซึ่งจะแปลลำดับเช่น\nเป็นบรรทัดใหม่ตามตัวอักษรก่อนที่โปรแกรมคำสั่งจะเห็นสตริง นั่นหมายความว่าสตริงดังกล่าวทำงานร่วมกับคำสั่งใดก็ได้รวมถึง old -e-less ธรรมดาecho:

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

แต่ในขณะที่พกพาได้ง่ายกว่าecho -eราคา ANSI ก็ยังคงเป็นส่วนขยายที่ไม่ใช่ POSIX

และอีกครั้งในขณะที่สิ่งเหล่านี้เป็นตัวเลือกทั้งหมดฉันชอบtee <<EOFวิธีแก้ปัญหาที่ตรงด้านบน


น่าสนใจ! คุณช่วยเพิ่มหมายเหตุอธิบายเหตุผลในการแยกไฟล์เป็น / dev / null ได้ไหม
knite

sudo bash -c 'foo >/some/file'ทำงานให้ฉันบน osx :-)
kayochin

ฉันชอบคำตอบนี้เพราะใช้ได้กับข้อความหลายบรรทัด cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

นั่นเป็นคำตอบสุดท้ายของฉันอย่างมีประสิทธิภาพยกเว้นคุณได้เพิ่มcatกระบวนการที่ไม่เกี่ยวข้อง :)
Mark Reed

17

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

เนื่องจากคำแนะนำข้างต้นไม่ชัดเจนฉันจึงใช้คำแนะนำจากบล็อกโพสต์นั้น ด้วยตัวอย่างเพื่อให้ง่ายต่อการดูสิ่งที่คุณต้องทำ

$ sudo cat /root/example.txt | gzip> /root/example.gz -bash
: /root/example.gz: ปฏิเสธการอนุญาต

สังเกตว่าเป็นคำสั่งที่สอง (คำสั่ง gzip) ในไปป์ไลน์ที่ทำให้เกิดข้อผิดพลาด นั่นคือที่มาของเทคนิคการใช้ bash กับตัวเลือก -c

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

เราสามารถดูรูปแบบเอาต์พุตของคำสั่ง ls ที่การสร้างไฟล์บีบอัดสำเร็จ

วิธีที่สองคล้ายกับวิธีแรกที่เราส่งสตริงคำสั่งเพื่อทุบตี แต่เรากำลังทำในไปป์ไลน์ผ่าน 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
สำหรับคำสั่งง่ายๆเช่นนี้อาจใช้ sh แทน bash ได้ดีกว่าเล็กน้อย เช่น sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz " แต่อย่างอื่นคำอธิบายที่ดี +1
bryce


1

ขั้นตอนที่ 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ตัวแปร

ไฟล์ bash ซอร์สSTE2

$ source write_pacman.sh

ขั้นตอนที่ 3เรียกใช้ฟังก์ชัน

$ write_pacman

คำพูดของ EOF คืออะไร?
bilabila

0

ต่อท้ายไฟล์ (sudo cat):

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

ต่อท้าย echo เข้ากับไฟล์ (sudo echo):

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

(EXTRA) ไม่คำนึงถึง ouput:

echo >origin> | sudo tee -a <target-file> >/dev/null
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.