วิธีรันคำสั่งเชลล์อย่างเงียบ ๆ ?


70

:!<command>สามารถใช้เพื่อรันคำสั่งในเชลล์ แต่นี่ "ใช้เวลา" terminal ของฉันและเติมด้วยstdoutคำสั่งเฉพาะที่

ฉันจะรันคำสั่งในพื้นหลังที่แจ้งให้ฉันทราบเฉพาะรหัสทางออกที่ไม่เป็นศูนย์ได้อย่างไร


1
คุณยินดีที่จะใช้ + python interface หรือหนึ่งใน interface ภาษาอื่นหรือไม่?
joeytwiddle

1
@joeytwiddle ใช่
OrangeTux

คำตอบ:


52

:silent exec "!command"

โปรดทราบว่าเซสชันเสียงเรียกเข้าของคุณจะยังคงถูกครอบครองขณะที่คำสั่งของคุณกำลังดำเนินการ นี่เป็นเพราะธรรมชาติของ Vim คุณยังสามารถกลับไปที่เชลล์ของคุณได้โดยกด CTRL + z (เพื่อส่ง Vim ไปที่พื้นหลัง) จากนั้นกลับมา vim ด้วยคำสั่งfgตามปกติ

ในการทำสิ่งต่าง ๆ แบบอะซิงโครนัสลองดูที่ปลั๊กอินvim-dispatchของ Tim Pope หรือโครงการ NeoVim ที่มีการสนับสนุนพื้นเมืองสำหรับการดำเนินการคำสั่งแบบอะซิงโครนัสซึ่งคุณสามารถใช้ประโยชน์จากปลั๊กอิน NeoMake ได้อย่างง่ายดาย เวอร์ชั่นล่าสุดของ Vim ยังรองรับการทำงานแบบอะซิงโครนัส

ดู: h: เงียบ


5
นี่เป็นสิ่งแรกที่ฉันพยายามเมื่อฉันเห็นคำถาม :-) แต่ถ้าคุณใช้คำสั่งที่ส่งออกไปยัง stdout (หรือ stderr) มันจะ "อุดตัน" หน้าต่าง Vim หลักของคุณ (ลอง:silent !ls) ... t' ให้ผลผลิตที่เหมาะสมใน-0 ไม่ใช่รหัสทางออก ... ดังนั้นผมเกรงว่ามันเป็นบิตที่เกี่ยวข้องมากขึ้นกว่าเพียงแค่ใช้:silent...
มาร์ติน Tournoij

โอ้ขอโทษ. เพิ่มหมายเหตุเกี่ยวกับการดำเนินการแบบอะซิงโครนัสในขณะที่คุณเขียนความคิดเห็น ไม่ทราบวิธีแก้ไขปัญหาสถานะการออก คุณอาจต้องการลอง #vim บน Freenode
OliverUv

ฉันต้องการทราบว่าไม่แสดง:silent exec "!ls"หรือไม่:silent !lsแสดงผลลัพธ์ใด ๆ เลยในเวอร์ชัน Vim ของฉัน 7.4 ด้วยแพตช์ 1-213
OliverUv

3
อืมนี่คือสิ่งที่ฉันได้รับหลังจาก:silent !ls: i.stack.imgur.com/1XvS6.png ... ฉันต้องกด^Lเพื่อแก้ไขอีกครั้ง ...
Martin Tournoij

เสียงเรียกเข้าส่งแน่นอนดูเหมือนว่าวิธีการแก้ปัญหาที่กำหนด
joeytwiddle

30

เพื่อรันคำสั่งโดยไม่ทริกเกอร์ข้อความ Enter เช่น:

กด ENTER หรือพิมพ์คำสั่งเพื่อดำเนินการต่อ

ลองตัวอย่างง่ายๆดังต่อไปนี้:

:silent !echo Hello

จากนั้นกดCtrl+ L(หรือ:redraw!) เพื่อรีเฟรชหน้าจอเมื่อกลับไปเป็นกลุ่ม

เพื่อหลีกเลี่ยงความต้องการรีเฟรชคุณสามารถกำหนดคำสั่งที่คุณกำหนดเองเช่น:

:command! -nargs=1 Silent execute ':silent !'.<q-args> | execute ':redraw!'

ตอนนี้คุณสามารถใช้คำสั่ง Vim ใหม่เพื่อรันคำสั่ง shell (หมายเหตุ: ไม่มี!และด้วย capital S ):

:Silent ps

แหล่งที่มา: การหลีกเลี่ยงข้อความแจ้ง "Hit ENTER to Continue"


1
คุณจะได้รับแจ้งเกี่ยวกับรหัสออกที่ไม่เป็นศูนย์ได้อย่างไร
Martin Tournoij

1
ไม่ใช่วิธีแก้ปัญหาสำหรับรหัสออกที่ไม่ใช่ศูนย์ แต่เพื่อเพิ่มสิ่งนี้คำสั่งเงียบนี้อนุญาตให้ใช้คำสั่งที่ต้องการ:Silent ctags -R . > /dev/null &รันในพื้นหลัง การส่ง stdout ไปที่ / dev / null ป้องกันไม่ให้เอาต์พุตปรากฏในบัฟเฟอร์ vim
ftvs

10

ทางออกที่รวดเร็วและสกปรก (ใน Vimscript บริสุทธิ์)

สิ่งนี้สามารถเริ่มต้นกระบวนการในพื้นหลัง:

:!slow_command_here > /tmp/output 2>&1 &

แต่เป็นกลุ่มต้องการวิธีการค้นหาเมื่อกระบวนการเสร็จสมบูรณ์และอย่างไรดังนั้นลองใช้ไฟล์มาร์กเกอร์:

:!(rm -f /tmp/finished; slow_command_here > /tmp/output 2>&1; echo "$?" > /tmp/finished) &

ตอนนี้เราสามารถขอให้กลุ่มตรวจสอบทุก ๆ ครั้งว่ากระบวนการเสร็จสมบูรณ์แล้วหรือไม่:

:augroup WaitForCompletion
:autocmd!
:autocmd CursorHold * if filereadable('/tmp/finished') | exec "augroup WaitForCompletion" | exec "au!" | exec "augroup END" | echo "Process completed with exit code ".readfile('/tmp/finished')[0] | end
:augroup END

เฮ้มันไม่สวย แต่เรียงลำดับจากผลงาน!

ข้อเสีย

น่าเสียดายที่ autocmd ด้านบนไม่ทำงานจนกว่าคุณจะเลื่อนเคอร์เซอร์ และถ้าคุณต้องการเรียกใช้กระบวนการพื้นหลังมากกว่าหนึ่งครั้งคุณจะต้องเพิ่ม ID เฉพาะให้กับไฟล์เหล่านั้นและไปยังชื่อกลุ่ม autocmd

ดังนั้นหากคุณสามารถรับมือกับการพึ่งพาพิเศษคุณอาจจะดีกว่าโดยใช้โซลูชันที่ทดลองและทดสอบแล้วเช่นปลั๊กอิน vim-dispatch ที่กล่าวถึงในคำตอบอื่น

กำลังแสดงผล

หากคุณต้องการที่จะเห็นผลลัพธ์ของกระบวนการแทนที่จะเป็นเพียงแค่รหัสออกจากนั้นแทนที่สุดท้ายechoข้างต้นด้วย:

silent botright pedit /tmp/output

ที่จะเปิดหน้าต่างแสดงตัวอย่าง หากต้องการใช้รายการข้อผิดพลาด quickfix แทน:

silent cget /tmp/output | copen

รายการ QuickFIX :cnextช่วยให้คุณสามารถนำทางไปยังข้อผิดพลาดโดยใช้ อย่างไรก็ตามcopenเลื่อนเคอร์เซอร์ของคุณไปที่หน้าต่าง Quickfix เมื่อมันเปิดขึ้นซึ่งอาจจะแปลกใจ / น่ารำคาญ

(วิธีแก้ปัญหาสำหรับสิ่งนั้นคือการเปิดหน้าต่าง QF ด้วย:copenเมื่อเริ่มต้นกระบวนการดังนั้นคุณจะไม่ต้องเรียกมันในตอนท้าย)


2
นี่เป็นคำตอบเดียวที่นี่ที่จริงตอบคำถาม: "รันคำสั่งในพื้นหลังที่แจ้งให้ฉันเพียงรหัสทางออกไม่เป็นศูนย์" ... ฉันไม่รู้ว่าทำไมคำตอบอื่น ๆ ยังมี upvotes เลย ...
Martin Tournoij

2
ผมเชื่อว่าพวกเขามี upvotes เพราะพวกเขาได้อย่างสมบูรณ์แบบตอบชื่อของคำถามซึ่งเป็นสิ่งที่จะนำผู้เข้าชมหน้านี้ "วิธีรันคำสั่งเชลล์เงียบ ๆ ?" ปรากฏการณ์ทาง SE ทั่วไป! ผู้เข้าชมรู้สึกขอบคุณ แต่อาจไม่ได้รับการลงโทษทางวินัย
joeytwiddle

ในโลกอุดมคติเราอาจมีคำถามสองข้อที่แยกกัน: "วิธีรันคำสั่งเชลล์อย่างเงียบ ๆ ?" และ "วิธีรันคำสั่งเชลล์ในพื้นหลัง" เพื่อให้ชาว Google สามารถค้นหาสิ่งที่พวกเขากำลังมองหา
joeytwiddle

6

ใช้คำสั่งในพื้นหลัง

ฉันกำลังรันคำสั่งที่บล็อกไว้เล็กน้อยและฉันไม่สนใจเกี่ยวกับผลลัพธ์ สิ่งนี้สามารถได้รับการดูแลโดยเริ่มต้นกระบวนการที่แนบมาไม่ใช่กับเทอร์มินัล / อีมูเลเตอร์ แต่ไปที่ระบบและเปลี่ยนทิศทางเอาต์พุตทั้งหมดไปยัง / dev / null ตัวอย่างเช่น:

:silent exec "!(espeak 'speaking in background'&) > /dev/null"

(...&)ทำงานในพื้นหลังและ> /dev/nullเปลี่ยนเส้นทางการส่งออกทั้งหมดไป/dev/null(อะไร)

วงเล็บจะดักเอาท์พุทไปไว้ที่ subshell (หรืออะไรทำนองนั้น) แต่มันมีผลข้างเคียงที่ไม่ได้ติดอยู่กับเปลือกปัจจุบัน (ไม่ใช่เรื่องใหญ่)

การรันคำสั่งแบบไม่มีการโต้ตอบในพื้นหลังด้วยการแม็พ

ฉันเพิ่งรู้ว่าหากคุณเป็นจริงในการทำแผนที่คุณสามารถทำสิ่งที่ชอบ

nnoremap <silent> <leader>e :!$(espeak 'speaking in the background'&) > /dev/null

ด้านบนจะไม่แสดงผลใด ๆ ในแถบคำสั่งและจะไม่แสดงสิ่งใด ๆ ในเทอร์มินัลนอกเสียงเรียกเข้า มันจะถูกแมปไป<leader> eซึ่ง\ eโดยค่าเริ่มต้น

ใช้คำสั่งจับผลลัพธ์ของมันแสดงเนื้อหา

(การแก้ไขอื่น - และอาจเป็นสิ่งที่ประณีตที่สุด) หนึ่งนี้จะแสดงการส่งออกของคำสั่งดังนั้นหากคุณเลือก:


ภายในแท็บใหม่:

silent exec "!(echo 'hello. I'm a process! :)') > /tmp/vim_process" | :tabedit /tmp/vim_process

ภายในแยกแนวตั้ง:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :vs /tmp/vim_process

ภายในแยก HORIZONTAL:

silent exec "!(echo 'hello. I'm a process :)') > /tmp/vim_process" | :sp /tmp/vim_process

... ทำสิ่งที่คุณต้องการ


5

หากคุณไม่สนใจรหัสออกคุณสามารถไปกับสิ่งนี้:

:call system('/usr/bin/zathura using-docker.pdf &')

1
หากคุณสนใจรหัสทางออกv:shell_errorตัวแปรจะบอกคุณ
Jerome Dalbert

4

ไม่แน่ใจว่าสิ่งนี้เหมาะสมกับความต้องการของคุณหรือไม่ (ไม่จัดการกระบวนการพื้นหลังในfoo & - ไม่แน่ใจว่านั่นคือสิ่งที่คุณหมายถึงโดย"ในพื้นหลัง" ) แต่คำสั่งที่กำหนดเองสามารถนำมาใช้ใน:

fun! s:PraeceptumTacet(cmd)
    silent let f = systemlist(a:cmd)
    if v:shell_error
        echohl Error
        echom "ERROR #" . v:shell_error
        echohl WarningMsg
        for e in f
            echom e
        endfor
        echohl None
    endif
endfun

command! -nargs=+ PT call s:PraeceptumTacet(<q-args>)

จากนั้นใช้เป็นเช่น:

:PT ls -l
:PT foobar
ERROR #127
/bin/bash: foobar: command not found

ถ้าคุณไม่จำเป็นต้อง / ต้องการให้ข้อความแสดงข้อผิดพลาดง่าย system()จะพอเพียงแล้วตรวจสอบและรายงานเช่นv:shell_errorecho Error!

จากวิธีใช้v: shell_error :

                                        v:shell_error shell_error-variable
v:shell_error   Result of the last shell command.  When non-zero, the last
                shell command had an error.  When zero, there was no problem.
                This only works when the shell returns the error code to Vim.
                The value -1 is often used when the command could not be
                executed.  Read-only.
                Example: >
    :!mv foo bar
    :if v:shell_error
    :  echo 'could not rename "foo" to "bar"!'
    :endif
                "shell_error" also works, for backwards compatibility.

สิ่งนี้ไม่ได้ทำงานอยู่เบื้องหลัง คุณยังต้องรอให้เสร็จ
Martin Tournoij

@Carpetsmoker: ใช่ดังนั้นความคิดเห็นของฉัน“... ไม่ได้จัดการกับกระบวนการพื้นหลังในขณะที่foo & ...” จากข้อความ Q โดยรวมฉันตีความว่ามันเป็นอย่างใดอย่างหนึ่งหรือ อย่างใดอย่างหนึ่ง“ดำเนินการเป็นพื้นหลัง AKA คำสั่งภายนอก”หรือ“ดำเนินการเป็น _and_ คำสั่งภายนอกเป็นกระบวนการพื้นหลัง.” ฉันตอบส่วนใหญ่ขึ้นอยู่กับชื่อของคำถาม ฯลฯ - และเพิ่มความคิดเห็นใน“ ไม่แน่ใจ…” - และปล่อยไว้ที่ OP เพื่อชี้แจงหากมีหรือ
Runium

3

เป็นกลุ่มที่ 8 แนะนำงานสนับสนุน หนึ่งสามารถเรียกใช้คำสั่งภายนอกในพื้นหลังโดยไม่ต้องพึ่งพาปลั๊กอิน ตัวอย่างเช่นการรันเซิร์ฟเวอร์ markdown ( markserv ) ที่ตำแหน่งปัจจุบันและไม่บล็อกเซสชัน vim:

:call job_start('markserv .')

สิ่งนี้เริ่มต้นกระบวนการ markserv เป็นกระบวนการย่อยของกระบวนการ vim ปัจจุบัน pstreeคุณสามารถตรวจสอบกับ

ดูjob_start


2

ฉันใช้รหัสต่อไปนี้:

command! -nargs=1 Silent call SilentExec(<q-args>)

function! SilentExec(cmd)
  let cmd = substitute(a:cmd, '^!', '', '')
  let cmd = substitute(cmd, '%', shellescape(expand('%')), '')
  call system(cmd)
endfunction

ตอนนี้คุณสามารถพิมพ์ดังต่อไปนี้

:Silent !your_command

นี้มีลักษณะเกือบจะเหมือนในตัวเป็นกลุ่มของคำสั่งยกเว้นสำหรับเงินทุนsilent ! Sมันยังช่วยให้ตัวเลือก!เพื่อให้ดูคล้ายกันมากขึ้น การเรียกเพื่อให้system()คำสั่งเชลล์เงียบอย่างแท้จริง: ไม่มีหน้าจอกะพริบและไม่มีการวาดซ้ำ

(และหากคุณต้องการรหัสสถานะคุณสามารถตรวจสอบv:shell_errorตัวแปรดูวิธีใช้สำหรับข้อมูลเพิ่มเติม)


1

AsyncRunปลั๊กอินถ้าออกแบบมาสำหรับการนี้จะช่วยให้คุณสามารถเรียกใช้คำสั่งเชลล์ในพื้นหลังใน Vim8 / NeoVim และแสดงผลในหน้าต่าง QuickFIX

เช่นเดียวกับการเปลี่ยน!คำสั่ง:

:AsyncRun ls -la /

คุณยังสามารถซ่อนผลลัพธ์ในหน้าต่าง quickfix ได้อย่างสมบูรณ์:

:AsyncRun -mode=3 ls -la /

เมื่องานเสร็จ AsyncRun จะแจ้งให้คุณทราบโดยผ่านตัวเลือก-post:

:AsyncRun -post=some_vim_script   ls -la /

สคริปต์กลุ่มที่กำหนดโดย-postจะถูกดำเนินการหลังจากงานเสร็จสิ้น

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