Gọi vi thông qua tìm | xargs phá vỡ thiết bị đầu cuối của tôi. Tại sao?


137

Khi gọi vimqua find | xargs, như thế này:

find . -name "*.txt" | xargs vim

bạn nhận được một cảnh báo về

Input is not from a terminal

và một thiết bị đầu cuối với khá nhiều hành vi bị hỏng sau đó. Tại sao vậy?


11
Lưu ý bên lề: Bạn có thể thực hiện thao tác này hoàn toàn trong vim, không sử dụng findhoặc hoàn toàn không sử dụng xargs. Mở vim không có đối số, sau đó chạy :args **/*.txt<CR>để đặt đối số của vim từ bên trong trình chỉnh sửa.
Trevor Powell

3
@TrevorPowell: Trong tất cả những năm này, vim không bao giờ hết làm tôi ngạc nhiên.
DevSolar



Câu trả lời:


100

Khi bạn gọi một chương trình thông qua xargs, stdin (đầu vào tiêu chuẩn) của chương trình sẽ trỏ đến /dev/null. (Vì xargs không biết stdin gốc , nên nó làm điều tốt nhất tiếp theo.)

$ đúng | xargs filan -s
    0 chrdev / dev / null
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ đúng | xargs ls -l / dev / fd /

Vim hy vọng stdin của nó giống như thiết bị đầu cuối điều khiển của nó và thực hiện trực tiếp các ioctl khác nhau liên quan đến thiết bị đầu cuối trên stdin. Khi được thực hiện trên /dev/null(hoặc bất kỳ mô tả tệp không tty nào), các ioctls đó là vô nghĩa và trả về ENOTTY, bị bỏ qua trong âm thầm.

  • Tôi đoán ở một nguyên nhân cụ thể hơn: Khi khởi động, Vim đọc và ghi nhớ các cài đặt thiết bị đầu cuối cũ và khôi phục chúng trở lại khi thoát. Trong tình huống của chúng tôi, khi "cài đặt cũ" được yêu cầu cho một fd không phải là tty (mô tả tệp), Vim nhận được tất cả các giá trị trống và tất cả các tùy chọn bị vô hiệu hóa, và vô tình đặt cùng một thiết bị đầu cuối của bạn.

    Bạn có thể thấy điều này bằng cách chạy vim < /dev/null, thoát nó, sau đó chạy stty, nó sẽ tạo ra rất nhiều <undef>s. Trên Linux, việc chạy stty sanesẽ làm cho thiết bị đầu cuối có thể sử dụng lại được (mặc dù nó sẽ bị mất các tùy chọn như vậy iutf8, có thể gây ra những phiền toái nhỏ sau này).

Bạn có thể coi đây là một lỗi trong Vim, vì nó có thể mở /dev/ttyđể kiểm soát thiết bị đầu cuối, nhưng không. (Tại một số thời điểm trong khi khởi động, Vim sao chép stderr của nó thành stdin, cho phép nó đọc các lệnh đầu vào của bạn - từ một fd được mở để viết - nhưng ngay cả điều đó không được thực hiện sớm.)


20
+1 và đối với TL; DR mọi người chỉ cần chạystty sane
doc_id

@rahmanisback: Các câu trả lời khác, cộng với nhận xét của Trevor, tất cả đều cung cấp các cách để tránh vỡ thiết bị đầu cuối ngay từ đầu. Tôi đã chấp nhận câu trả lời của grawity, bởi vì câu hỏi của tôi là "tại sao", chứ không phải "làm thế nào để tránh" - điều đó được bao phủ bởi một câu hỏi khác thực sự sinh ra câu hỏi này.
DevSolar 11/2/2015

@DevSolar Hiểu, nhưng hãy nghĩ về những người thất vọng như tôi, người chỉ cần google làm thế nào để thoát khỏi hành vi đó trong khi không may mắn - có đủ thời gian ngay bây giờ để nghiên cứu "tại sao", dù sao cũng rất thú vị.
doc_id

4
Khi thiết bị đầu cuối của tôi bị hỏng, như thế này, tôi sử dụng resetthay vì stty sanevà nó hoạt động tốt sau đó.
Capi Etheriel

137

(Theo lời giải thích của grawity, điều đó xargschỉ stdinra /dev/null.)

Các giải pháp cho vấn đề này là thêm -otham số để xargs. Từ man xargs:

-o

      Mở lại stdin như /dev/ttytrong tiến trình con trước khi thực hiện lệnh. Điều này rất hữu ích nếu bạn muốn xargschạy một ứng dụng tương tác.

Do đó, dòng mã sau sẽ phù hợp với bạn:

tìm thấy . -name "* .txt" | xargs -o vim

GNU xargs hỗ trợ phần mở rộng này kể từ một số bản phát hành năm 2017 (với tên tùy chọn dài --open-tty).

Đối với các phiên bản cũ hơn hoặc các phiên bản khác của xargs, bạn có thể chuyển vào rõ ràng /dev/ttyđể giải quyết vấn đề:

find . -name "*.txt" | xargs bash -c '</dev/tty vim "$@"' ignoreme

(Có ignoremeở đó để chiếm $ 0, do đó $ @ là tất cả các đối số từ xargs.)


2
Làm thế nào bạn sẽ tạo ra một bí danh bash từ này? $@dường như không dịch chính xác các đối số.
zanegray

1
@zanegray - bạn không thể tạo bí danh, nhưng bạn có thể biến nó thành một hàm. Hãy thử:function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
Christopher

Để được giải thích chi tiết về cách giải pháp GNU xargs hoạt động và lý do tại sao bạn cần ignoremechuỗi giả , hãy xem vi.stackexchange.com/a/17813
wvducky

@zanegray, Bạn có thể biến nó thành bí danh. Các trích dẫn là khó khăn. Xem giải pháp tại vi.stackexchange.com/a/17813
wvducky

The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.(Nó không có sẵn trên macOS đối với tôi vì tôi đã cài đặt xargs từ homebrew (GNU GNU))
localhostdotdev

33

Cách dễ nhất:

vim $(find . -name "*foo*")

5
Câu hỏi chính là "tại sao", không phải "làm thế nào để tránh nó" và nó đã được trả lời cho sự hài lòng hai năm rưỡi trước.
DevSolar

5
Điều này, tất nhiên, không hoạt động đúng khi tên tệp chứa khoảng trắng hoặc các ký tự đặc biệt khác và cũng là một rủi ro bảo mật.
Dejay Clayton

1
Câu trả lời yêu thích của tôi vì nó hoạt động cho mọi lệnh liệt kê các tệp, không chỉ là "tìm" hoặc ký tự đại diện. Nó đòi hỏi một chút tin tưởng, như Dejay chỉ ra.
Travis Wilson

1
Điều này sẽ không hoạt động với nhiều trường hợp sử dụng xargs được thiết kế cho: ví dụ: khi số lượng đường dẫn rất cao (cc @TravisWilson)
Người tốt

21

Nó sẽ hoạt động tốt nếu bạn sử dụng tùy chọn -exec trên find chứ không phải đường ống vào xargs.

find . -type f -name filename.txt -exec vi {} + 

2
Huh ... mẹo có +(thay vì "thông thường" \;) để đưa tất cả các tệp được tìm thấy vào một phiên Vim - một tùy chọn tôi tiếp tục quên. Bạn đúng, tất nhiên, và +1 cho điều đó. Tôi sử dụng vim $(find ...)đơn giản là theo thói quen. Tuy nhiên, tôi đã thực sự hỏi về lý do tại sao các hoạt động đường ống làm hỏng thiết bị đầu cuối, và grawity đóng đinh điều đó với lời giải thích của mình.
DevSolar

2
Đây là câu trả lời tốt nhất và nó hoạt động trên cả BSD / OSX / GNU / Linux.
kevinarpe

1
Ngoài ra, find không phải là cách duy nhất để có được danh sách các tệp phải được chỉnh sửa đồng thời bởi vim. Tôi có thể sử dụng grep để tìm tất cả các tệp có mẫu và thử chỉnh sửa chúng cùng một lúc.
Chandranshu

8

Sử dụng GNU Parallel thay thế:

find . -name "*.txt" | parallel -j1 --tty vim

Hoặc nếu bạn muốn mở tất cả các tệp trong một lần:

find . -name "*.txt" | parallel -Xj1 --tty vim

Nó thậm chí còn xử lý chính xác các tên tệp như:

My brother's 12" records.txt

Xem video giới thiệu để tìm hiểu thêm: http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
Không có mặt khắp nơi. Hầu hết thời gian tôi đang làm việc trên các máy chủ mà tôi không được tự do cài đặt các công cụ bổ sung. Nhưng dù sao cũng cảm ơn vì gợi ý.
DevSolar

Nếu bạn đang tự do làm tập tin 'cat>; chmod + x file 'thì bạn có thể cài đặt GNU Parallel: Nó chỉ đơn giản là một tập lệnh perl. Nếu bạn muốn các trang man và như vậy, bạn có thể cài đặt nó theo homedir của mình: ./cool --prefix = $ HOME && make && make install
Ole Tange

2
OK, đã thử điều đó - nhưng song song không mở tất cả các tệp, nó sẽ mở chúng liên tiếp . Nó cũng khá vừa miệng cho một hoạt động đơn giản. vim $(find . -name "*.txt")đơn giản hơn và bạn nhận được tất cả các tệp được mở cùng một lúc.
DevSolar

5
@DevSolar: Hơi không liên quan, nhưng cả hai find | xargs$(find)sẽ có vấn đề lớn với khoảng trắng trong tên tệp.
grawity

2
@grawity Đúng, nhưng không có cách nào dễ dàng xung quanh nó (mà tôi biết). Bạn sẽ phải bắt đầu nghịch ngợm $IFS, -print0và các thứ, và sau đó bạn rời khỏi vương quốc của một giải pháp dòng lệnh một phát và đạt đến điểm mà bạn nên đưa ra một kịch bản ... có một lý do tại sao các không gian trong tên tệp không được khuyến khích .
DevSolar

0

có thể không phải là tốt nhất nhưng ở đây nó là kịch bản tôi sử dụng (có tên vim-open):

#!/usr/bin/env ruby

require 'shellwords'

inputs = (ARGV + (STDIN.tty? ? [] : STDIN.to_a)).map(&:strip)
exec("</dev/tty vim #{inputs.flatten.shelljoin}")

sẽ làm việc với vim-open a b cls | vim-openví dụ


Đối với một số câu trả lời khác, lưu ý rằng câu hỏi thực tế là "tại sao", chứ không phải "làm thế nào để tránh nó". (Ví mà tôi vẫn sẽ trỏ đến bình luận Trevor dưới câu hỏi của tôi là cách chắc chắn nhất mà không yêu cầu kịch bản, bí danh hoặc bất cứ điều gì.)
DevSolar
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.