Làm thế nào để thực thi lệnh shell một cách âm thầm?


70

:!<command>có thể được sử dụng để thực thi một lệnh trong shell. Nhưng điều này "chiếm lấy" thiết bị đầu cuối của tôi và lấp đầy nó bằng stdoutlệnh cụ thể đó.

Làm cách nào để tôi thực thi một lệnh trong nền chỉ thông báo cho tôi về mã thoát không khác không?


1
Bạn có sẵn sàng sử dụng giao diện + python hoặc một trong các giao diện ngôn ngữ khác không?
joeytwiddle

1
@joeytwiddle Có
OrangeTux 19/2/2015

Câu trả lời:


52

:silent exec "!command"

Lưu ý rằng phiên vim của bạn sẽ vẫn bị chiếm trong khi lệnh của bạn đang thực thi. Điều này là do bản chất đồng bộ của Vim. Bạn vẫn có thể quay lại trình bao của mình bằng cách nhấn CTRL + z (để gửi Vim xuống nền) và sau đó tiếp tục vim bằng lệnh fgnhư bình thường.

Để thực hiện mọi thứ một cách không đồng bộ, hãy xem vim- mail của Tim Pope hoặc NeoVim dự án có hỗ trợ riêng để thực thi lệnh không đồng bộ, mà bạn có thể dễ dàng tận dụng bằng cách sử dụng plugin NeoMake. Phiên bản mới nhất của Vim cũng có hỗ trợ cho các tác vụ không đồng bộ.

Xem : h: im lặng


5
Đây cũng là điều đầu tiên tôi thử khi thấy câu hỏi :-) Nhưng nếu bạn sử dụng lệnh xuất ra thiết bị xuất chuẩn (hoặc stderr), nó sẽ "chặn" cửa sổ Vim chính của bạn (thử :silent !ls) ... Nó cũng không Tôi không cung cấp đầu ra thích hợp cho mã thoát không 0 ... Vì vậy, tôi e rằng nó liên quan nhiều hơn một chút so với việc chỉ sử dụng :silent...
Martin Tournoij

Ồ xin lỗi. Đã thêm một số lưu ý về thực thi không đồng bộ trong khi bạn viết nhận xét của mình. Không biết làm thế nào để giải quyết vấn đề thoát trạng thái. Bạn có thể muốn thử trong #vim trên Freenode.
OliverUv

Tôi cũng muốn lưu ý rằng không phải :silent exec "!ls"cũng không :silent !lshiển thị bất kỳ đầu ra nào trên phiên bản Vim, 7.4 của tôi với các bản vá 1-213.
OliverUv

3
Hừm, đây là những gì tôi nhận được sau :silent !ls: i.stack.imgur.com/1XvS6.png ... Tôi cần nhấn ^Lđể sửa lại ...
Martin Tournoij

vim-công chắc chắn trông giống như một giải pháp cho vấn đề nhất định.
joeytwiddle

30

Để thực thi lệnh mà không kích hoạt thông báo Enter, như:

Nhấn ENTER hoặc gõ lệnh để tiếp tục

thử ví dụ đơn giản sau:

:silent !echo Hello

Sau đó nhấn Ctrl+ L(hoặc :redraw!) để làm mới màn hình khi quay lại Vim.

Để tránh cần làm mới, bạn có thể xác định lệnh tùy chỉnh của riêng mình, như:

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

Bây giờ, bạn có thể sử dụng lệnh Vim mới để chạy lệnh shell (lưu ý: không có ! Và với chữ S ):

:Silent ps

Nguồn: Tránh lời nhắc "Nhấn ENTER để tiếp tục" tại trang web Vim Wikia


1
Làm thế nào để bạn nhận được thông báo về mã thoát không khác không?
Martin Tournoij

1
Không phải là một giải pháp cho mã thoát khác không, nhưng để thêm vào điều này, lệnh Im lặng này cho phép các lệnh muốn :Silent ctags -R . > /dev/null &chạy trong nền. Gửi stdout đến / dev / null ngăn đầu ra xuất hiện trong bộ đệm vim.
ftvs

10

Một giải pháp nhanh chóng và bẩn thỉu (trong Vimscript thuần túy)

Điều này có thể bắt đầu một quá trình trong nền:

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

Nhưng Vim cần một cách để tìm hiểu khi nào quá trình hoàn thành và làm thế nào, vì vậy hãy sử dụng tệp đánh dấu:

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

Bây giờ chúng tôi có thể yêu cầu Vim kiểm tra thường xuyên xem liệu quy trình đã hoàn thành chưa:

: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

Này, nó không đẹp, nhưng nó là một tác phẩm!

Nhược điểm

Thật không may, autocmd ở trên sẽ không kích hoạt cho đến khi bạn di chuyển con trỏ. Và nếu bạn muốn chạy nhiều quá trình nền cùng một lúc, bạn sẽ cần thêm ID duy nhất vào các tệp đó và vào tên nhóm autocmd.

Vì vậy, nếu bạn có thể xử lý một phụ thuộc bổ sung, bạn có thể tốt hơn bằng cách sử dụng giải pháp đã thử và thử nghiệm như plugin vim-Clark được đề cập trong câu trả lời khác.

Hiển thị đầu ra

Nếu bạn muốn xem đầu ra của quá trình, thay vì chỉ mã thoát , sau đó thay thế cuối cùng echoở trên bằng:

silent botright pedit /tmp/output

Điều đó sẽ mở cửa sổ xem trước. Để sử dụng danh sách lỗi quickfix thay thế:

silent cget /tmp/output | copen

Danh sách quickfix cho phép bạn dễ dàng điều hướng đến các lỗi sử dụng :cnext. Tuy nhiên, copendi chuyển con trỏ của bạn vào cửa sổ quickfix khi nó mở ra, điều này có thể sẽ gây ngạc nhiên / khó chịu.

(Một cách giải quyết khác là sẽ mở cửa sổ QF :copenkhi bắt đầu quá trình, vì vậy bạn sẽ không cần gọi nó ở cuối.)


2
Đây là câu trả lời duy nhất ở đây thực sự trả lời cho câu hỏi: "thực thi một lệnh trong nền chỉ thông báo cho tôi về mã thoát không bằng 0" ... Tôi không biết tại sao các câu trả lời khác thậm chí còn có cả ...
Martin Tournoij

2
Tôi tin rằng họ có upvote vì họ trả lời hoàn hảo tiêu đề của câu hỏi, đó là những gì mang lại cho khách truy cập vào trang này. "Làm thế nào để thực thi lệnh shell một cách im lặng?" Một hiện tượng SE phổ biến! Du khách rất biết ơn, nhưng có lẽ không kỷ luật.
joeytwiddle

Trong một thế giới lý tưởng, có lẽ chúng ta sẽ có hai câu hỏi riêng biệt: "Làm thế nào để thực thi các lệnh shell một cách im lặng?" và "Làm thế nào để thực thi các lệnh shell trong nền?" để các nhân viên của Google có thể tìm thấy những gì họ đang tìm kiếm.
joeytwiddle

6

Chạy một lệnh trong nền

Tôi đã chạy một lệnh bị chặn một chút và tôi không thực sự quan tâm đến đầu ra. Điều này có thể được quan tâm bằng cách bắt đầu quá trình gắn liền với thiết bị đầu cuối / trình giả lập mà thay vào đó là hệ thống và chuyển hướng tất cả đầu ra sang / dev / null. Ví dụ:

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

các (...&)chạy nó trong nền và > /dev/nullchuyển hướng tất cả các đầu ra /dev/null(không có gì).

Dấu ngoặc đơn bẫy đầu ra thành một lớp con (hoặc một cái gì đó tương tự), nhưng nó có tác dụng phụ là không được gắn vào vỏ hiện tại (không phải là vấn đề lớn).

Chạy một lệnh âm thầm trong nền với ánh xạ

Tôi chỉ nhận ra rằng trên thực tế, bạn đang lập bản đồ cho nó, bạn có thể làm một cái gì đó như

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

Ở trên sẽ không hiển thị gì trong thanh lệnh và ngoài ra, sẽ không hiển thị bất cứ thứ gì trong thiết bị đầu cuối bên ngoài vim. Nó sẽ được ánh xạ tới <leader> e, \ etheo mặc định.

Chạy một lệnh, nắm bắt đầu ra của nó, hiển thị nội dung của nó

(Một chỉnh sửa khác - và có thể là chỉnh sửa gọn gàng nhất). Cái này sẽ hiển thị các kết quả của một lệnh nếu bạn lựa chọn:


TRONG MỘT TAB MỚI:

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

TRONG MỘT SPLIT CHỨNG NHẬN:

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

Bên trong một SPLIT HORIZONTAL:

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

... làm bất cứ điều gì bạn muốn


5

Nếu bạn không quan tâm đến mã thoát, bạn có thể sử dụng mã này:

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

1
Nếu bạn quan tâm đến mã lối ra, các v:shell_errorbiến sẽ cho bạn biết
Jerome Dalbert

4

Không chắc chắn nếu điều này phù hợp với nhu cầu của bạn, (không xử lý các quy trình nền như trong foo & - không chắc đó có phải là ý của bạn trong phạm vi nền ) hay không, nhưng một lệnh tùy chỉnh có thể được sử dụng như trong:

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>)

Sau đó sử dụng như:

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

Nếu bạn không cần / muốn thông báo lỗi đơn giản system()có thể đủ, thì hãy kiểm tra v:shell_errorvà báo cáo bằng ví dụ echo Error!.

Từ trợ giúp 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.

Đây không thực sự chạy nó trong nền; bạn vẫn cần đợi nó kết thúc
Martin Tournoij

@Carpetsmoker: Vâng, như vậy nhận xét của tôi “... không xử lý quá trình nền như trong foo &...” . Từ toàn bộ văn bản Q tôi đã giải thích nó là hoặc. Hoặc là Thực thi như một lệnh bên ngoài AKA nền tảng Hay hoặc Thực thi như một lệnh bên ngoài _and_ làm quá trình nền . Tôi đã trả lời chủ yếu dựa trên tiêu đề của Q, v.v. - và đã thêm một nhận xét về Chế độ không chắc chắn trên phạm vi - và để lại cho OP để làm rõ nếu hoặc.
Runium

3

Vim 8 giới thiệu việc làm hỗ trợ. Người ta có thể chạy lệnh bên ngoài trong nền mà không cần dựa vào plugin. Ví dụ: để chạy máy chủ markdown ( markerv ) tại vị trí hiện tại và không chặn phiên vim:

:call job_start('markserv .')

Điều này bắt đầu quá trình markerv như là một quy trình phụ của quy trình vim hiện tại. Bạn có thể xác minh điều này với pstree.

Xem job_start


2

Tôi sử dụng mã sau đây:

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

Bây giờ bạn có thể gõ như sau

:Silent !your_command

Điều này trông gần giống như silent !lệnh tích hợp của Vim , ngoại trừ thủ đô S. Nó thậm chí còn cho phép một tùy chọn !để làm cho nó trông giống nhau hơn nữa. Cuộc gọi để system()thực hiện lệnh shell thực sự im lặng: không có màn hình nhấp nháy và không vẽ lại.

(và nếu bạn cần mã trạng thái, bạn có thể kiểm tra v:shell_errorbiến, xem trợ giúp để biết thêm thông tin)


1

Các AsyncRun Plugin nếu được thiết kế cho điều này, nó cho phép bạn chạy các lệnh shell trong nền trong Vim8 / NeoVim và hiển thị kết quả trong cửa sổ QuickFix.

Cũng như một sự thay thế của !lệnh:

:AsyncRun ls -la /

Bạn cũng có thể ẩn hoàn toàn đầu ra trong cửa sổ quickfix:

:AsyncRun -mode=3 ls -la /

Khi công việc kết thúc, AsyncRun sẽ thông báo cho bạn bằng cách chuyển một tùy chọn -post:

:AsyncRun -post=some_vim_script   ls -la /

Kịch bản vim được xác định bởi -postsẽ được thực thi sau khi hoàn thành công việc.

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.