เสียงเรียกว่า“ เขียนด้วย sudo” ของกลุ่มทำงานอย่างไร?


1410

หลายคนคงเคยเห็นคำสั่งที่ให้คุณเขียนไฟล์ที่ต้องการการอนุญาตรูทถึงแม้ว่าคุณจะลืมเปิด vim ด้วย sudo:

:w !sudo tee %

สิ่งคือฉันไม่ได้รับสิ่งที่เกิดขึ้นตรงนี้

ฉันคิดแล้ว: wสำหรับสิ่งนี้

                                                        *:w_c* *:write_c*
:[range]w[rite] [++opt] !{cmd}
                        Execute {cmd} with [range] lines as standard input
                        (note the space in front of the '!').  {cmd} is
                        executed like with ":!{cmd}", any '!' is replaced with
                        the previous command |:!|.

ดังนั้นจึงผ่านทุกบรรทัดเป็นอินพุตมาตรฐาน

!sudo teeส่วนสายteeที่มีสิทธิ์ผู้ดูแลระบบ

สำหรับทุกคนที่จะทำให้ความรู้สึก%ควรออกชื่อไฟล์ (เป็นพารามิเตอร์สำหรับtee) แต่ฉันไม่สามารถหาการอ้างอิงเกี่ยวกับความช่วยเหลือสำหรับพฤติกรรมนี้

tl; drมีใครช่วยฉันตัดคำสั่งนี้ออกไหม


5
@ นาธาน: จะ:w !sudo cat > %ไม่ทำงานเช่นกันและไม่ก่อมลพิษเอาท์พุทมาตรฐาน?
Bjarke Freund-Hansen

51
@bjarkef - ไม่ไม่ทำงาน ในกรณีนั้นsudoจะใช้กับcatแต่>ไม่อนุญาตดังนั้นจึงไม่อนุญาต คุณสามารถลองใช้คำสั่งทั้งหมดใน subshell sudo เช่น:w !sudo sh -c "cat % > yams.txt"แต่ที่ไม่ทำงานอย่างใดอย่างหนึ่งเพราะใน subshell %เป็นศูนย์; คุณจะลบเนื้อหาในไฟล์ของคุณออก
นาธานลอง

3
ฉันแค่ต้องการที่จะเพิ่มว่าหลังจากพิมพ์คำสั่งนั้นข้อความเตือนอาจปรากฏขึ้น ถ้าเป็นเช่นนั้นกด L จากนั้นคุณจะถูกขอให้กด Enter ทำและในที่สุดคุณจะบันทึกไฟล์ของคุณ
pablofiumara

9
@NathanLong @knittl: :w !sudo sh -c "cat >%"ใช้งานได้จริงเช่นเดียวกับsudo tee %เพราะ Vim ใช้แทนชื่อไฟล์%ก่อนที่มันจะเข้าสู่ subshell อย่างไรก็ตามทั้งคู่ไม่ทำงานหากชื่อไฟล์มีช่องว่างอยู่ คุณต้องทำ:w !sudo sh -c "cat >'%'"หรือ:w !sudo tee "%"แก้ไข
ฮันโซล -Oh

1
บันทึกโดยใช้: W แล้วโหลดไฟล์อีกครั้ง: คำสั่ง W: ดำเนินการ ': silent w! sudo tee%> / dev / null' | : แก้ไข!
Diego Roberto Dos Santos

คำตอบ:


1610

ใน:w !sudo tee %...

% หมายถึง "ไฟล์ปัจจุบัน"

ในฐานะที่เป็นยูปีชี้ให้เห็น , %ไม่แน่นอนหมายถึง "ชื่อไฟล์ปัจจุบัน" ซึ่งจะถูกส่งไปteeเพื่อที่จะรู้ว่าไฟล์ที่จะเขียนทับ

(ในคำสั่งทดแทนมันแตกต่างกันเล็กน้อยตามที่:help :%แสดงเป็นequal to 1,$ (the entire file)(ขอบคุณ @Orafu สำหรับการชี้ให้เห็นว่าสิ่งนี้ไม่ได้ประเมินชื่อไฟล์) ตัวอย่างเช่น:%s/foo/barหมายถึง " ในไฟล์ปัจจุบันให้แทนที่fooด้วยbar" หากคุณไฮไลต์ ข้อความก่อนพิมพ์:sคุณจะเห็นว่าเส้นที่ไฮไลต์เกิดขึ้น%แทนช่วงการแทนที่ของคุณ)

:w ไม่ได้อัปเดตไฟล์ของคุณ

ส่วนที่สับสนของเคล็ดลับนี้คือคุณอาจคิดว่า:wกำลังแก้ไขไฟล์ของคุณ แต่ไม่ใช่ หากคุณเปิดและแก้ไขfile1.txtแล้ววิ่ง:w file2.txtมันจะเป็น "บันทึกเป็น"; file1.txtจะไม่ได้รับการแก้ไข file2.txtแต่เนื้อหาบัฟเฟอร์ปัจจุบันจะถูกส่งไป

แทนที่จะfile2.txtคุณสามารถทดแทนคำสั่งของเชลล์ที่จะได้รับเนื้อหาบัฟเฟอร์ ตัวอย่างเช่น:w !catเพิ่งจะแสดงเนื้อหา

หาก Vim ไม่ได้ทำงานด้วยการเข้าถึง sudo :wจะไม่สามารถแก้ไขไฟล์ที่ได้รับการป้องกันได้ แต่ถ้ามันส่งเนื้อหาบัฟเฟอร์ไปยังเชลล์คำสั่งในเชลล์สามารถรันด้วย sudoได้ teeในกรณีนี้เราจะใช้

เข้าใจที

สำหรับteeรูปภาพteeคำสั่งเป็นไพพ์รูปตัว T ในสถานการณ์การไพพ์ bash ปกติ: มันส่งเอาต์พุตไปยังไฟล์ที่ระบุและส่งไปยังเอาต์พุตมาตรฐานซึ่งสามารถดักจับได้ด้วยคำสั่ง piped ถัดไป

ยกตัวอย่างเช่นในps -ax | tee processes.txt | grep 'foo'รายการของกระบวนการที่จะถูกเขียนไปยังแฟ้มข้อความและgrepผ่านไปพร้อมกับ

     +-----------+    tee     +------------+
     |           |  --------  |            |
     | ps -ax    |  --------  | grep 'foo' |
     |           |     ||     |            |
     +-----------+     ||     +------------+
                       ||   
               +---------------+
               |               |
               | processes.txt |
               |               |
               +---------------+

(ไดอะแกรมที่สร้างขึ้นด้วยAsciiflow )

ดูteeหน้าคนสำหรับข้อมูลเพิ่มเติม

ตี๋เป็นแฮ็ค

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

ทำให้เคล็ดลับนี้ง่าย

คุณสามารถเพิ่มนี้เพื่อคุณ.vimrcเพื่อให้เคล็ดลับนี้ง่ายต่อการใช้งาน: :w!!เพียงแค่ชนิด

" Allow saving of files as sudo when I forgot to start vim using sudo.
cmap w!! w !sudo tee > /dev/null %

> /dev/nullส่วนหนึ่งอย่างชัดเจนพ่นออกมาตรฐานการส่งออกตั้งแต่ที่ผมกล่าวว่าเราไม่จำเป็นต้องผ่านอะไรที่จะสั่งประปาอีก


147
โดยเฉพาะอย่างยิ่งเช่นสัญลักษณ์ของคุณ "w !!" ซึ่งง่ายต่อการจดจำหลังจากใช้ "sudo !!" บนบรรทัดคำสั่ง
Aidan Kane

11
ดังนั้นนี่ใช้teeสำหรับความสามารถในการเขียน stdin ไปยังไฟล์ ฉันประหลาดใจที่ไม่มีโปรแกรมที่ต้องทำ (ฉันพบโปรแกรมที่ฉันไม่เคยได้ยินชื่อspongeที่ทำสิ่งนี้) ฉันเดาว่าโดยทั่วไป "เขียนสตรีมไปยังไฟล์" จะดำเนินการโดยเชลล์ในตัว Vim !{cmd}ไม่ได้แยกเปลือก (ฟอร์กcmdแทน) หรือไม่? บางทีอาจจะเป็นสิ่งที่มีความชัดเจนมากขึ้นจะใช้บางอย่างที่แตกต่างกันในการทำงานของมากกว่าsh -c ">" tee
Steven Lu

2
@Steven Lu: spongeเป็นส่วนหนึ่งของmoreutilsแพ็กเกจในทุก ๆ การแจกจ่ายยกเว้นดิสทริบิวชันจากเดเบียน moreutilsมีเครื่องมือที่ดีพอสมควรพร้อมกับเครื่องมือทั่วไปเพิ่มเติมเช่นxargsและ tee
สวิส

10
วิธีขยายนามแฝงนี้เพื่อบอก vim ให้โหลดเนื้อหาไฟล์ที่เปลี่ยนแปลงไปยังบัฟเฟอร์ปัจจุบันโดยอัตโนมัติได้อย่างไร? มันถามฉันว่าจะทำอย่างไรให้มันเป็นไปโดยอัตโนมัติ?
Zlatko

10
@ user247077: ในกรณีนี้ให้catรันเป็น root และเอาต์พุตจะถูกเปลี่ยนเส้นทางโดยเชลล์ซึ่งไม่ได้ทำงานเป็น root echo hi > /read/only/fileมันเป็นเช่นเดียวกับ
WhyNotHugo

99

ในบรรทัดคำสั่งดำเนินการ%ย่อมาจากชื่อไฟล์ปัจจุบัน เอกสารนี้มีไว้ใน:help cmdline-special:

In Ex commands, at places where a file name can be used, the following
characters have a special meaning.
        %       Is replaced with the current file name.

ตามที่คุณค้นพบเรียบร้อยแล้ว:w !cmdไพพ์เนื้อหาของบัฟเฟอร์ปัจจุบันไปยังคำสั่งอื่น อะไรteeคือการคัดลอกอินพุตมาตรฐานไปยังหนึ่งไฟล์ขึ้นไปและไปยังเอาต์พุตมาตรฐาน ดังนั้น:w !sudo tee % > /dev/nullเขียนเนื้อหาของบัฟเฟอร์ปัจจุบันไปยังไฟล์ปัจจุบันอย่างมีประสิทธิภาพในขณะที่เป็นรูท คำสั่งอื่นที่สามารถใช้ได้สำหรับสิ่งนี้คือdd:

:w !sudo dd of=% > /dev/null

เป็นทางลัดคุณสามารถเพิ่มการจับคู่นี้ใน.vimrc:

" Force saving files that require root permission 
cnoremap w!! w !sudo tee > /dev/null %

ด้วยการข้างต้นคุณสามารถพิมพ์:w!!<Enter>เพื่อบันทึกไฟล์เป็นราก


1
น่าสนใจ:help _%นำสิ่งที่คุณป้อนเข้ามา แต่:help %แสดงคีย์การจับคู่กับวงเล็บปีกกา ฉันไม่คิดว่าจะลองนำหน้าขีดเส้นใต้นั่นเป็นรูปแบบบางอย่างในเอกสาร vim หรือไม่? มีสิ่งอื่นที่ 'พิเศษ' ให้ลองเมื่อค้นหาความช่วยเหลือหรือไม่?
David Pope

2
@David: helpคำสั่งข้ามไปยังแท็ก :h help-tagsคุณสามารถดูแท็กสามารถใช้ได้กับ คุณยังสามารถใช้ความสมบูรณ์ของบรรทัดคำสั่งเพื่อดูแท็กที่ตรงกัน: :h cmdline<Ctrl-D>(หรือ:h cmdline<Tab>ถ้าคุณตั้งค่าwildmodeตามนั้น)
Eugene Yarmash

1
ฉันต้องใช้cmap w!! w !sudo tee % > /dev/nullในไฟล์. vimrc ของฉันเพื่อใช้งานได้ คือ%ใส่ผิดในคำตอบข้างต้นหรือไม่ (ไม่มีผู้เชี่ยวชาญที่เป็นกลุ่มที่นี่.)
dmmfll

@DMfll ใช่มันเป็น คำสั่งในคำตอบจะส่งผลsudo tee > /dev/null /path/to/current/fileซึ่งไม่สมเหตุสมผล (กำลังจะแก้ไขนั้น)
jazzpi

1
@jazzpi: คุณผิด เชลล์ไม่สนใจว่าในบรรทัดคำสั่งคุณทำการเปลี่ยนเส้นทางไฟล์ที่ใด
Eugene Yarmash

19

สิ่งนี้ยังใช้งานได้ดี:

:w !sudo sh -c "cat > %"

นี่คือแรงบันดาลใจจากความคิดเห็นของ @Nathan Long

ประกาศ :

" ต้องใช้แทน 'เพราะเราต้องการ%ขยายก่อนส่งไปยัง shell


11
ขณะนี้อาจใช้งานได้ แต่ยังช่วยให้ sudo เข้าถึงหลายโปรแกรม (sh และ cat) ตัวอย่างอื่น ๆ อาจมีความปลอดภัยมากกว่าโดยการแทนที่teeด้วย/usr/bin/teeเพื่อป้องกันการโจมตีแบบ PATH
idbrii

18

:w - เขียนไฟล์

!sudo - เรียกคำสั่งเชลล์ sudo

tee- คำสั่งเอาต์พุตของการเขียน (vim: w) ถูกเปลี่ยนทิศทางโดยใช้ tee % ไม่มีอะไรนอกจากชื่อไฟล์ปัจจุบันเช่น /etc/apache2/conf.d/mediawiki.conf ในคำอื่น ๆ คำสั่ง tee ถูกเรียกใช้เป็นรูทและใช้อินพุตมาตรฐานและเขียนลงในไฟล์ที่แสดงโดย% อย่างไรก็ตามสิ่งนี้จะแจ้งให้โหลดไฟล์อีกครั้ง (กด L เพื่อโหลดการเปลี่ยนแปลงเป็นกลุ่ม):

ลิงค์การสอน


9

คำตอบที่ได้รับการยอมรับนั้นครอบคลุมทั้งหมดดังนั้นฉันจะให้อีกตัวอย่างหนึ่งของทางลัดที่ฉันใช้สำหรับบันทึก

เพิ่มลงในetc/vim/vimrc(หรือ~/.vimrc):

  • cnoremap w!! execute 'silent! write !sudo tee % >/dev/null' <bar> edit!

ที่ไหน:

  • cnoremap: บอกเป็นกลุ่มว่าจะต้องเชื่อมโยงทางลัดต่อไปนี้ในบรรทัดคำสั่ง
  • w!!: ทางลัดเอง
  • execute '...': คำสั่งที่รันสตริงต่อไปนี้
  • silent!: เรียกใช้มันอย่างเงียบ ๆ
  • write !sudo tee % >/dev/null: คำถาม OP เพิ่มการเปลี่ยนเส้นทางข้อความไปNULLยังเพื่อสร้างคำสั่งใหม่ทั้งหมด
  • <bar> edit!: เคล็ดลับนี้เป็นเชอร์รี่ของเค้ก: มันเรียกยังเป็นeditคำสั่งเพื่อโหลดบัฟเฟอร์แล้วหลีกเลี่ยงข้อความดังกล่าวเป็นบัฟเฟอร์ที่มีการเปลี่ยนแปลง <bar>คือวิธีการเขียนสัญลักษณ์ไปป์เพื่อแยกสองคำสั่งที่นี่

หวังว่ามันจะช่วย ดูเพิ่มเติมสำหรับปัญหาอื่น ๆ :


เงียบ! ปิดใช้งานพรอมต์รหัสผ่านเพื่อให้คุณไม่เห็น
Andy Ray

7

ฉันอยากจะแนะนำวิธีอื่นในการ"Oups ฉันลืมที่จะเขียนsudoในขณะที่เปิดไฟล์ของฉัน"ปัญหา:

แทนที่จะได้รับpermission denied, และต้องพิมพ์:w!!, ฉันคิดว่ามันดีกว่าที่จะมีvimคำสั่งแบบมีเงื่อนไขซึ่งsudo vimถ้าเป็นเจ้าของไฟล์rootถ้าเจ้าของไฟล์

นี่เป็นเรื่องง่ายที่จะนำไปใช้ (อาจจะมีการใช้งานที่หรูหรากว่าฉันเห็นได้ชัดว่าไม่ใช่ bash-guru):

function vim(){
  OWNER=$(stat -c '%U' $1)
  if [[ "$OWNER" == "root" ]]; then
    sudo /usr/bin/vim $*;
  else
    /usr/bin/vim $*;
  fi
}

และมันใช้งานได้ดีจริงๆ

นี่เป็นbashวิธีการที่เน้นศูนย์กลางมากกว่าvim - ดังนั้นทุกคนอาจไม่ชอบมัน

แน่นอน:

  • มีกรณีการใช้งานที่มันจะล้มเหลว (เมื่อเจ้าของไฟล์ไม่ได้ rootแต่ต้องsudoแต่ฟังก์ชั่นสามารถแก้ไขได้)
  • มันไม่สมเหตุสมผลเมื่อใช้vimสำหรับไฟล์แบบอ่านอย่างเดียว (เท่าที่ฉันกังวลฉันใช้tailหรือcatสำหรับไฟล์ขนาดเล็ก)

แต่ฉันพบนี้นำดีมากประสบการณ์ของผู้ใช้ devซึ่งเป็นสิ่งที่ IMHO bashมีแนวโน้มที่จะถูกลืมเมื่อใช้ :-)


5
เพิ่งทราบว่านี่เป็นการให้อภัยน้อยกว่ามาก โดยส่วนตัวแล้วความผิดพลาดส่วนใหญ่ของฉันคือความผิดพลาดที่โง่ ดังนั้นฉันชอบที่จะหัวขึ้นเมื่อฉันทำอะไรบางอย่างที่อาจโง่และเป็นผลสืบเนื่อง แน่นอนว่านี่เป็นเรื่องของความพึงพอใจ แต่การเพิ่มระดับสิทธิพิเศษจะเป็นการกระทำที่ขยันขันแข็ง นอกจากนี้: หากคุณประสบกับปัญหานี้บ่อยจนทำให้ ": w !!" พอที่จะรบกวน sudo อัตโนมัติอย่างเงียบ ๆ (แต่ถ้าเจ้าของ = root); คุณอาจต้องการตรวจสอบเวิร์กโฟลว์ปัจจุบันของคุณ
Payne

ความคิดเห็นที่น่าสนใจแม้ว่าจะมีใครสนใจเพราะเมื่อไฟล์ถูกเปิดเป็นrootรหัสผ่านจะถูกสอบถาม
Augustin Riedinger

อา! มีความแตกต่าง ขึ้นอยู่กับว่าผู้ใช้ sudo ตั้งค่า "NOPASSWD" หรือไม่
Payne

การมี NOPASSWD คือการให้อภัยน้อยกว่า ... :)
Augustin Riedinger

4

สำหรับ NEOVIM

เนื่องจากปัญหาเกี่ยวกับการโทรแบบโต้ตอบ ( https://github.com/neovim/neovim/issues/1716 ) ฉันใช้สิ่งนี้กับ neovim ตามคำตอบของ Dr Beco:

cnoremap w!! execute 'silent! write !SUDO_ASKPASS=`which ssh-askpass` sudo tee % >/dev/null' <bar> edit!

จะเป็นการเปิดกล่องโต้ตอบโดยใช้การssh-askpassขอรหัสผ่าน sudo

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.