찾기를 통해 vi를 호출 | xargs가 터미널을 끊습니다. 왜?


137

vim통해 호출 할 때 다음 find | xargs과 같이하십시오.

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

당신은에 대한 경고를받을

Input is not from a terminal

그리고 이후에 거의 고장난 터미널이 있습니다. 왜 그런 겁니까?


11
참고 :이 작업은 전적으로 vim 내 에서 사용 find하거나 전혀 사용하지 않을 수 있습니다 xargs. 인수없이 vim을 연 다음 :args **/*.txt<CR>편집기 내부에서 vim의 인수를 설정하기 위해 실행 합니다.
트레버 파월

3
@TrevorPowell : 몇 년 동안, vim은 결코 나를 놀라게하지 않았습니다.
DevSolar



답변:


100

를 통해 프로그램을 호출 xargs하면 프로그램의 표준 입력 (표준 입력)이을 가리 킵니다 /dev/null. (xargs는 원래 stdin을 알지 못하므로 다음으로 최선을 다합니다.)

$ 진실 | xargs filan -s
    0 chrdev / dev / null
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ 진실 | xargs ls -l / dev / fd /

Vim은 stdin이 제어 터미널과 동일 할 것으로 기대하고 stdin에서 다양한 터미널 관련 ioctl을 직접 수행합니다. 에 완료되면 /dev/null(또는 비 청각 장애 파일 기술자), 그 ioctl의 의미가 있으며 자동으로 무시됩니다 ENOTTY를 반환합니다.

  • 더 구체적인 원인에 대한 나의 추측 : 시작할 때 Vim은 이전 터미널 설정을 읽고 기억하고 종료 할 때 다시 복원합니다. 이 상황에서 tty가 아닌 fd (파일 설명자)에 대해 "이전 설정"이 요청되면 Vim은 모든 값을 비우고 모든 옵션을 사용할 수 없게 설정하고 터미널에 동일하게 설정합니다.

    을 실행 vim < /dev/null하고 종료 한 다음 실행 stty하면이 모든 것을 볼 수 있습니다 <undef>. Linux에서 실행은 stty sane(는 있지만 다시 터미널 사용할 수를 만들 것입니다 같은 옵션을 잃었을 iutf8가능성이 나중에 사소한 불만의 원인).

터미널 제어를 위해 열 수는/dev/tty 있지만 Vim에서는이 버그를 고려할 는 없습니다. (시작하는 동안 어느 시점에서 Vim은 stderr을 stdin으로 복제하여 쓰기를 위해 열린 fd에서 입력 명령을 읽을 수는 있지만 충분히 일찍 수행되지는 않습니다.)


20
+1, TL; DR의 경우 DR 사람들은stty sane
doc_id

@rahmanisback : 다른 답변과 Trevor의 의견은 모두 처음부터 터미널 손상을 피할 수있는 방법을 제공했습니다. 내 질문이 "피하는 방법"이 아니라 "왜"였기 때문에 grawity의 대답을 받아 들였습니다 . 실제로이 질문낳은 다른 질문으로 덮여 있습니다.
DevSolar

@DevSolar 이해하지만, 불행히도, 그 행동을 제거하는 방법을 구글에있는 좌절 된 사람들에 대해 생각하십시오. 불행히도, 지금 "왜"를 공부할 충분한 시간이 있습니다. 그럼에도 불구하고 매우 흥미 롭습니다.
doc_id

4
내 터미널이 이렇게 끊어지면 reset대신 대신 사용 stty sane하고 그 후에 잘 작동합니다.
Capi Etheriel

136

(grawity의 설명,에 이어 xargs지적 stdin/dev/null.)

솔루션 이 문제는 추가하는 것입니다 -o에 매개 변수를 xargs. 보낸 사람 man xargs:

-o

      /dev/tty명령을 실행하기 전에 하위 프로세스에서와 같이 stdin을 다시여십시오 . xargs대화식 응용 프로그램을 실행 하려는 경우에 유용합니다 .

따라서 다음 코드 줄이 작동합니다.

검색 . 이름 "* .txt"| xargs -o vim

GNU xargs는 2017 년 일부 릴리스 (긴 옵션 이름 --open-tty) 이후이 확장을 지원합니다 .

이전 버전 또는 다른 버전의 xargs의 경우 명시 적으로 전달 /dev/tty하여 문제를 해결할 수 있습니다.

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

( ignoreme$ 0을 차지하므로 $ @는 xargs의 모든 인수입니다.)


2
이것으로 bash 별칭을 어떻게 만들 수 있습니까? $@인수를 올바르게 번역하지 않는 것 같습니다.
zanegray

1
@zanegray-별칭을 만들 수는 없지만 기능을 만들 수 있습니다. 시도 :function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; }
크리스토퍼

당신이 더미가 필요한 이유는 GNU의 xargs를 솔루션의 작동 방법에 대한 자세한 설명 및 ignoreme문자열을 참조 vi.stackexchange.com/a/17813을
wisbucky

@zanegray, 별명으로 만들 수 있습니다. 따옴표는 까다 롭습니다. vi.stackexchange.com/a/17813의
wisbucky

The -J, -o, -P and -R options are non-standard FreeBSD extensions which may not be available on other operating systems.(homebrew (GNU)에서 xargs를 설치했기 때문에 macOS에서는 사용할 수 없었습니다)
localhostdotdev

33

가장 쉬운 방법 :

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

5
주요 질문은 "피하는 방법"이 아니라 "왜"였으며, 2 년 반 전에 만족에 대한 답변을 받았습니다.
DevSolar

5
물론 파일 이름에 공백이나 다른 특수 문자가 포함되어 있으면 보안 기능이 제대로 작동하지 않습니다.
Dejay Clayton

1
내가 가장 좋아하는 대답은 "찾기"나 와일드 카드뿐만 아니라 파일을 나열하는 모든 명령에서 작동하기 때문입니다. Dejay가 지적했듯이 약간의 신뢰가 필요합니다.
Travis Wilson

1
이것은 많은 사용 사례의 xargs를 함께 설계되었습니다 작동하지 않습니다 수 있습니다 : 예를 들어, 경로의 수는 (CC의 @TravisWilson) 매우 높을 때
좋은 사람

21

xargs로 파이프하는 대신 찾기에 -exec 옵션을 사용하면 제대로 작동합니다.

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

2
허 ... 트릭이있다 +(대신 "보통"의 \;에 발견 된 모든 파일을 얻을) 하나 개의 옵션은 내가 - 빔 세션 계속 잊고. 물론 그렇습니다. 나는 vim $(find ...)단순히 습관에서 벗어납니다. 그러나 실제로 파이프 작업으로 인해 터미널이 망가지는 이유를 묻고 있었으며 Grawity는 설명을 통해 그 사실을 파악했습니다.
DevSolar

2
이것이 가장 좋은 대답이며 BSD / OSX / GNU / Linux에서 작동합니다.
kevinarpe

1
또한 find는 vim에 의해 동시에 편집되어야하는 파일 목록을 얻는 유일한 방법은 아닙니다. grep을 사용하여 패턴이있는 모든 파일을 찾고 동시에 편집 할 수도 있습니다.
Chandranshu

8

대신 GNU Parallel을 사용하십시오.

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

또는 한 번에 모든 파일을 열려면 다음을 수행하십시오.

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

심지어 다음과 같은 파일 이름을 올바르게 처리합니다.

My brother's 12" records.txt

자세한 내용은 소개 동영상을 참조하십시오 . http://www.youtube.com/watch?v=OpaiGYxkSuQ


1
어디서나 사용할 수 없습니다. 대부분의 날 나는 추가 도구를 자유롭게 설치할 수없는 서버에서 작업하고 있습니다. 그러나 어쨌든 힌트를 주셔서 감사합니다.
DevSolar

'cat> file'을 수행 할 자유가있는 경우 chmod + x file '을 선택하면 GNU Parallel을 설치할 수 있습니다. 단순히 perl 스크립트입니다. 매뉴얼 페이지 등을 원한다면 homedir 아래에 설치하면된다 : ./configure --prefix = $ HOME && make && make install
Ole Tange

2
OK, 그 시도 -하지만 병렬 않습니다 하지 모든 파일을 열고, 그것은을 열 않습니다 연속 . 간단한 조작을 위해서는 꽤 입이 많습니다. vim $(find . -name "*.txt")더 간단하고 모든 파일을 한 번에 열 수 있습니다.
DevSolar

5
@DevSolar : 다소 관련이없는,하지만 모두 find | xargs$(find)파일 이름에 공백이 큰 문제가됩니다.
grawity

2
@ grawity 맞지만, 그 주위에 쉬운 방법은 없습니다 (내가 아는 것). $IFS, -print0및 것들을 다루기 시작한 다음 원샷 명령 줄 솔루션의 영역을 떠나 스크립트를 생각해 내야 할 시점에 도달했습니다 ... 파일 이름의 공백이 권장되지 않는 이유가 있습니다. .
DevSolar

0

아마도 최고는 아니지만 여기서 내가 사용하는 스크립트 ( vim-open) :

#!/usr/bin/env ruby

require 'shellwords'

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

로 작동 vim-open a b c하고 ls | vim-open, 예를 들어


다른 답변들에 관해서는, 실제 질문은 "피하는 방법"이 아니라 "이유"였습니다. (스크립팅, 별명 또는 다른 것을 요구하지 않는 가장 확실한 방법으로 내 질문에 대한 Trevor의 의견을 계속 지적합니다.)
DevSolar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.