題 通過查找|來調用vi xargs打破了我的終端。為什麼?


調用時 vim 通過 find | xargs, 像這樣:

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

你得到一個警告

Input is not from a terminal

以及後來幾乎破壞了行為的終端。這是為什麼?


123
2017-09-15 16:26


起源


附註:您可以完全在vim中執行此操作,而不是使用 find 要么 xargs 一點都不打開沒有參數的vim,然後運行 :args **/*.txt<CR>從編輯器中設置​​vim的參數。 - Trevor Powell
@TrevorPowell:這些年來,vim從未停止讓我驚訝。 - DevSolar
有關: grep -l .. | xargs vim 產生警告,為什麼? 在unix SE - kenorb
有關: 在用xargs調用Vim之後終端被塞了 在Vim SE。 - kenorb
GitHub錯誤報告: vim不處理設置為/ dev / null的STDIN。 - kenorb


答案:


通過時調用程序 xargs,程序的標準輸入(標準輸入)指向 /dev/null。 (因為xargs不知道 原版的 stdin,它做了下一個最好的事情。)

$ true | xargs的 菲蘭 -s
    0 chrdev / dev / null
    1 tty / dev / pts / 1
    2 tty / dev / pts / 1

$ true | xargs ls -l / dev / fd /

Vim期望它的stdin與其控制終端相同,並執行各種與終端相關的操作 IOCTL直接在stdin上。完成後 /dev/null (或任何非tty文件描述符),那些ioctl是無意義的並返回ENOTTY,它會被靜默忽略。

  • 我猜一個更具體的原因:在啟動時,Vim讀取並記住舊的終端設置,並在退出時恢復它們。在我們的情況下,當為非tty fd(文件描述符)請求“舊設置”時,Vim會將所有值清空並禁用所有選項,並不小心將相同的值設置到您的終端。

    你可以通過運行看到這一點 vim < /dev/null退出,然後運行 stty,這將輸出很多 <undef>秒。在Linux上運行 stty sane 將使終端再次可用(儘管它  失去了這樣的選擇 iutf8,可能會導致輕微的煩惱)。

你可以認為這是Vim中的一個錯誤,因為它 能夠 打開 /dev/tty 對於終端控制,但沒有。 (在啟動期間的某個時刻,Vim將其stderr複製到stdin,這允許它讀取你的輸入命令 - 從寫入的fd開始 - 但即使這樣做也不夠早。)


85
2017-09-15 16:41



......蓬勃發展。非常感謝你! - DevSolar
+1,對於TL; DR人員只是跑 stty sane - rahmanisback
@rahmanisback:其他答案,加上Trevor的評論,都提供了避免終端破損的方法。我接受了grawity的答案,因為我的問題是“為什麼”,而不是“如何避免” - 這是由我所覆蓋的 另一個問題 實際上 催生 這個。 - DevSolar
@DevSolar明白了,但想想像我這樣的沮喪的人只是谷歌如何擺脫這種行為,而不是 - 不幸的是 - 現在有足夠的時間研究“為什麼”,儘管如此,這是非常有趣的。 - rahmanisback
當我的終端壞了,像這樣,我用 reset 代替 stty sane 之後它工作正常。 - Capi Etheriel


接下來是grawity的回答, xargs 點 stdin 至 /dev/null


來自OSX / BSD man xargs

-o在子進程中重新打開stdin為/ dev / tty
        在執行命令之前。這很有用
        如果你想讓xargs運行一個交互式應用程序。

因此,以下代碼行應該適合您:

找 。 -name“* .txt”| xargs -o vim

對於GNU man xargs 沒有標誌,但我們可以顯式傳入/ dev / tty來解決問題:

找 。 -name“* .txt”| xargs bash -c'</ dev / tty vim“$ @”'ignoreme

ignoreme是佔用$ 0,所以$ @是來自xargs的所有參數


122
2018-05-23 12:15



你會如何創建一個bash別名呢? $@ 似乎沒有正確翻譯參數。 - zanegray
@zanegray - 你不能創建一個別名,但你可以使它成為一個函數。嘗試: function vimin () { xargs sh -c 'vim "$@" < /dev/tty' vim; } - Christopher
有關GNU xargs解決方案如何工作的詳細說明,以及為什麼需要虛擬解決方案 ignoreme 字符串,請參閱 vi.stackexchange.com/a/17813 - wisbucky
@zanegray,你可以把它變成別名。報價很棘手。見解決方案 vi.stackexchange.com/a/17813 - wisbucky


最簡單的方法:

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

29
2018-03-08 02:13



主要問題是“為什麼”,而不是“如何避免它”,並且它在兩年半前得到滿足。 - DevSolar
當然,當文件名包含空格或其他特殊字符時,這不能正常工作,並且還存在安全風險。 - Dejay Clayton
我最喜歡的答案是因為它適用於列出文件的每個命令,而不僅僅是“查找”或通配符。 Dejay指出,這確實需要一點信任。 - Travis Wilson
這不適用於xargs設計用於的許多用例:例如,當路徑數量非常高時(cc @TravisWilson) - Good Person


如果你在find上使用-exec選項而不是管道進入xargs,它應該可以正常工作。 例如

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


20
2018-03-10 14:31



嗯......那裡有訣竅 + (而不是“通常的” \;)將所有找到的文件放入 一 Vim會話 - 一個選項我 保持 忘記了。當然,你是對的,並為此+1。我用 vim $(find ...) 只是出於習慣。但是,我實際上是在要求 為什麼 管道操作擰緊了終端,並且用他的解釋確定了重力。 - DevSolar
這是最好的答案,它適用於BSD / OSX / GNU / Linux。 - kevinarpe
此外,查找不是獲取必須由vim同時編輯的文件列表的唯一方法。我可以使用grep查找帶有模式的所有文件,並嘗試同時編輯它們。 - Chandranshu


使用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


8
2017-09-16 17:45



不是普遍可用的。大部分時間我在服務器上工作,我無法安裝其他工具。但無論如何,謝謝你的暗示。 - DevSolar
如果您可以隨意做“貓”檔案; chmod + x file'然後你可以安裝GNU Parallel:它只是一個perl腳本。如果你想要手冊頁等,你可以在你的homedir下安裝它:./ configure --prefix = $ HOME && make && make install - Ole Tange
好的,嘗試過 - 但並行確實如此 不 打開所有文件,它會打開它們 陸續。對於簡單的操作來說,它也非常滿口。 vim $(find . -name "*.txt") 更簡單,您可以立即打開所有文件。 - DevSolar
@DevSolar:有點無關,但兩者都有 find | xargs 和 $(find) 文件名中的空格會有很大的問題。 - grawity
@grawity正確,但沒有簡單的方法(我知道)。你必須開始擺弄 $IFS, -print0 和東西,然後你離開了一次性命令行解決方案的領域,並達到了你應該想出一個腳本...這就是為什麼不鼓勵文件名中的空格的原因。 - DevSolar