Переносим функциональность bash в cmd.exe

    Мне, как и многим из вас, работать с bash и стандартными утилитами Linux гораздо удобнее и приятнее, чем с cmd.exe. Однако, к сожалению, порой обстоятельства складываются так, что операционную систему выбирать не приходится. Например, в моём случае корпоративным стандартом является Windows 7. К счастью есть способ сделать жизнь линуксоида в командной строке Windows комфортнее, о нём и пойдет речь ниже.


    Прежде всего, при переходе из bash в cmd.exe доставляет неудобства ограниченность самой командной оболочки. Чтоб вставить текст из буфера обмена нужно тянуться к мышке, не хватает возможностей автодополнения, история хранится только в пределах одной сессии, к тому же не работает Ctrl+R и другие возможности работы с историей команд в bash.

    Исправить ситуацию нам поможет clink. Это Open Source утилита расширяющая возможности cmd.exe. Вот некоторые ее фичи:

    • Автодополнение по нажатию Tab. Возможности автодополнения могут быть расширены за счет пользовательских скриптов на Lua
    • Вставка текста из буфера по нажатию Ctrl-V (к сожалению Shift+Insert не работает)
    • Продвинутая работа с историей команд. Поиск по истории (Ctrl-R and Ctrl-S). Поддержка таких выражений как !!, !<string> и !$
    • Сохранение предыдущих сессий


    Авторы говорят, что clink протестирован только на Windows XP SP3, но я пользуюсь им уже год на Windows 7 и всё работает нормально.

    Скачали. Установили. Стало лучше, теперь cmd себя ведет почти как bash. Но кое-чего по прежнему не хватает. А именно привычных unix-утилит. Таких как cat, ls, tail, diff, grep, less, sort, wget и т.д. Некоторые утилиты имеют свои Windows-аналоги, например, вместо grep можно использовать findstr, но к ним придется привыкать заново, другие же аналогов вообще не имеют.

    Эту проблему нам поможет решить готовый набор программ UnxUtils. Набор включает в себя множество популярных в Unix и Linux инструментов командной строки (полный список можно посмотреть по этой ссылке) а так же несколько дополнительных программ.

    В частности, к дополнительным программам относятся pclip.exe и gclip.exe предназначенные для работы с буфером обмена Windows. Например вот так: pclip | sed "s/string1/string2/g" | gclip можно заменить все вхождения string1 на string2 в тексте хранящемся в буфере обмена.

    Для того чтоб получить всё это на свою Windows-машину нужно скачать архив UnxUtils.zip, распаковать его содержимое в какой-то каталог и добавить в переменную среды PATH путь к usr\local\wbin\, т.к. именно там хранятся исполняемые файлы.

    Для самых ленивых есть способ еще проще: скопируйте содержимое каталога usr\local\wbin\ из архива в каталог %WINDIR%\system32\ на вашей машине.
    Внимание! Если вы решили поступить именно так, то я не рекомендую заменять системные файлы на одноименные файлы из архива без четкого понимания того, что вы делаете.

    Всё! Осталось только запустить cmd и эффективно работать используя свой linux-опыт. Конечно есть и другие способы добиться того же самого результата, я описал тот, который нахожу максимально простым и удовлетворяющим все мои потребности.
    Поделиться публикацией

    Комментарии 103

      +7
      Полная коллекция unxutils.sourceforge.net/
        –16
        Перекидываем в Windows/System32 и пользуемся.
          +38
          Уж лучше PATH поправить, чем систем32 загаживать.
        +26
        А чем Cygwin не устроил? Он предоставляет даже привычное дерево ФС.
          0
          Пробовал Cygwin, не понравилось, т.к. нужно тогда вседа использовать именно его шелл.
          Остановился на GnuWin + Far.
            +3
            Почему всегда-то? Все эти программы и в cmd работают, разве что с запуском символьных ссылок есть проблемы.
              0
              Если в PATH добавить, то могут быть проблемы из-за его find. ЕМНИП, Qt не соберется в таком случае.
            +2
            +1 к Cygwin, все что надо есть, mintty в качестве терминала, шелл ставим любой, я настраивал zsh с плюшками, виндовому cmd даже и не снилось
            +5
            А мне больше ConEmu нравится. Юниксовые утилиты конечно отдельно, тут чисто терминал.
              +1
              Мне эта замечательная программа тоже очень понравилась. Тем, кто еще ее не попробовал, рекомендую прочитать этот топик: habrahabr.ru/post/164687/

              Консолька не только имеет поддержку множества вкладок, но и отлично кастомизируется. Лично я настроил ее в стиле консоли из Quake, т.е. окно выезжает сверху экрана по нажатию «Ctrl + ~». На мой взгляд это не только эффектно выглядит, но и очень удобно в использовании. В качестве юниксовых утилит использую MSYS, который поставляется с MinGW.

              Превьюшка:
                –2
                глючит он ужасно и давно не поддерживается.
                  +4
                  Кто не поддерживается? MSYS? Насколько я знаю, ведутся работы над MSYS2 в котором, в частности, юникод обещают.
                    –2
                    ConEmu. Вы за веткой-то следите вообще?
                      +2
                      Я ее только что прочитал. И не понятно, зачем вы бред пишете. Последний билд ConEmu был выложен 5 дней назад.
                        +2
                        Ох я идиот, я с C2 перепутал…
                    +6
                    Её запуск telnet уводит в ступор. И я, к несчастью, знаю, почему. На MSDN об этом ничего нет, и даже не пытайтесь искать.

                    На самом деле, терминал в windows — та ещё хрень. Недавно пришлось столкнуться с переадресацией консоли хоть куда-нибудь — выяснилось, что делать это можно по-хорошему только в анонимный пайп. А для анонимных пайпов не реализовано overlapped i\o — поэтому читать и писать можно только синхронно c помощью чудесной функции ReadFile.

                    Вы скажете — очевидно же, делай чтение в отдельном потоке! Но нифига — функция ReadFile не принимает НИКАКИХ параметров, касающихся таймаутов, а системные таймауты в случае чтения из потока не работают. Более того, ReadFile покорно ждет наполнения буфера, и #10#13 для неё — не аргумент для flush. Так что в выводе приходится отключать буферизацию. Но это не отменяет того, что ReadFile при ожидании ввода просто виснет.

                    Т.е. поток, зависший в readfile можно убить только через TerminateThread, и никаких тебе возможностей для корректного завершения — соответственно получаем memory leak в чистом виде.

                    Вы спросите — а почему нельзя грохнуть child process, чтобы readfile упал с broken pipe? Правильный ответ — он не упадёт, а будет висеть. При трассировке выяснилось, что там внутри есть WaitForSingleObject(hRead, INFINITE), соответственно при досрочной инвалидации hRead мы хрен чего дождемся. Эта проблема таинственным образом выскакивает, когда на один процесс порождено больше одного потока, перехватывающего stdi\o\err — readfile отпускает только после того, как прибиты все «перехваченные» дочерние процессы.

                    Таким образом, мы практически никак не можем завершить child process таким образом, чтобы потом нормально убраться в порожденном потоке. Но голь на выдумки хитра. Путём хитрой игры с хэндлами можно при вызове CreateProcess перенаправить stdi\o\err в три пары именованных пайпов — по одному на чтение\запись для stdin, stdout и stderr. Дальше мы можем делать с ними Overlapped i\o, а чтобы не грузить процессор в цикле ожидания — при ERROR_IO_PENDING использовать WaitForMultipleObjects для хэндлов всех шести именованных пайпов с timeout, например, в тысячу.

                    А дальше смотрим в WaitResult — и если там есть WAIT_TIMEOUT — проверяем существование процесса с PID из PROCESSINFORMATION, который мы заботливо сохранили после запуска CreateProcess. Ну и всякий там broken pipe тоже перехватить можно. Так что о том, что у нас что-то сломалось, приложение гарантированно узнает через одну секунду, которая пришла вторым параметром в WaitForMultipleObjects.

                    А дальше был секс с буферизацией и синхронизацией пайпов для ввода и вывода. Но это уже другая песня.

                    Таким нехитрым образом мы всего-то асинхронно перенаправили консоль. Задачка, для которой были тысячи готовых решений в интернетах в три строчки, на деле оказалась геморроем длиной в пару дней.

                    А мораль проста — держитесь от редиректоров виндоконсоли подальше. Она настолько крива, что никакими прямыми руками это не исправишь — где-нибудь, да вылезет косяк. Да что там говорить, даже собственный telnet-сервер от MS не в состоянии её корректно редиректнуть.
                      +2
                      Даю подсказку, корректный редирект консоли можно сделать средством:
                      — запускаем процесс через CreateProcess
                      — получаем HANDLE процесса
                      — делаем инджект через Detours или другую библиотеку
                      — Список того что нужно хукать — msdn.microsoft.com/en-us/library/windows/desktop/ms682073%28v=vs.85%29.aspx

                      В этом случае Вы 100% сможете перехватить консоль почти любой программы.
                      Также обернуть вызовы этих функций в буферы :)
                      И не забывайте что хуки для x86/x64 будут разные — разника в call conversion
                        0
                        Была такая мысль, но стояла задача не эмулировать полноценную консоль, а просто перехватить вывод. Банальный редирект stdout, который в этих ваших линуксах на баше делается одним символом

                        Ну и навскидку через пайпы всё-таки попроще получилось. А что мешало майкрософту сделать консоль виртуальной, с подключаемым терминалом, для меня до сих пор загадка.
                          0
                          дело в том что MS и не планировала делать функционал консоли подобным образом.
                          Изначально она пошла со времен 3.11, после в 95/98/2000 тоже запускалась по MSDOS принципу.
                          А вот уже в новых ОС — весь этот фунционал был заменен через Console API.
                          В Linux есть псевдотерминалы — да и вообще UNIX-way подразумевает «все есть файлы» — поэтому и работа в этом плане проще.
                          +1
                          И я тут ещё подумал — если из этой консоли будет запущен процесс, работающий с уже выделенной консолью напрямую через API (тот же Telnet), то его функции не будут похучены, и соответственно ничего мы не увидим.
                          Т.е. хучить придётся не конкретное приложение, а пилить system-wide hook, срабатывающий по дескриптору конкретной консоли.
                            –1
                            если новые процессы не создают новые экземпляры консоли и пишут все в один дескриптор — то можно обойтись и одним хуком.
                              0
                              Да ничего подобного. Родительское приложение (в частности, cmd.exe) висит в WaitForSingleObject в ожидании окончания работы запущенного процесса (в частности, telnet.exe). В таком подвешенном состоянии оно знать не знает ни о каких чтениях\записях в дескриптор. А телнет, в свою очередь, штука интерактивная, и ждет ввода от юзера. В итоге в консоли тишина. Собственно, насколько я понимаю, именно это явление мы и наблюдаем при попытке запустить telnet в предложенном выше редиректоре.

                                0
                                Собственно Я описал Выше — если новое приложения запускает AllocConsole — то вот тогда оно не будет хорошо работать :)
                          0
                          Её запуск telnet уводит в ступор. И я, к несчастью, знаю, почему. На MSDN об этом ничего нет, и даже не пытайтесь искать.

                          Неправда. «Ступор» может возникать только в Win7 и только в том случае, если вы отказались включить инжекты. Включите флажок и telnet «внезапно» заработает в ConEmu.

                          Кстати, проблема не из-за ReadFile, тайматутов пайпов и прочего…
                          Это последствия глюка 7-ки работы с буферами (CreateConsoleScreenBuffer), кому интересно — могут почитать подробнее.
                        +1
                        Я использую также ConEmu в связке с MinTTY + Cygwin. Получается практически полноценный линуксовый терминал с табами. А если нужен настоящий линукс, то открываю таб с автологином в виртуалку: META+N -> выпадает список тасков, выбираю ssh_local (в этом таске прописано: C:\Cygwin\bin\mintty.exe C:\Cygwin\bin\bash.exe -c «ssh user@localhost» ). В итоге сразу получаем таб в ConEmu с линуксовым окружением.
                          +1
                          Только есть ещё маленький недостаток: при переключении на ConEmu фокус ввода получает само окно, а не таб. Соответственно в MinTTY курсор показывается как неактивный
                            0
                            Специально для таких как вы была сделана опция «Focus in child windows».
                              0
                              Эта опция у меня включена вообще-то, но эффекта нет, к сожалению.
                                0
                                Аааа, нашел. Чего раньше не жаловались?
                                Пока можно хоткей использовать «Set focus to child GUI application» (вкладка Keys & Macro)
                                  0
                                  Стеснялся. И так хорошая штука!

                                  Хоткей попробовал: нужно каждый раз нажимать (это не удобно). Нашёл два тикета на code.google.com: 888 и 943. Они вроде бы как связаны с этой проблемой, но об опции в настройках ни слова.
                                  Может создать там issue?
                                    0
                                    Не надо. Build 130421.
                                      0
                                      Спасибо! Уже скачал — работает шикарно!
                      +1
                      За сlink спасибо, стандартный автокомплит ужасен.
                        +7
                        установка mingw msys даст нормальный терминал с практически полным набором утилит
                          0
                          Я за время свою сборку уже собрал, а все началось с гита, потом к нему в bin переехали все утилиты которых не хватало (например wget)
                          0
                          Чем PowerShell не угодил?
                            +13
                            unix-way как-то ближе.
                              0
                              Чем? Не холивара ради, но если вы смотрели на PowerShell, и он чем-то не нравится, хотелось бы знать, чем именно.
                                +2
                                тем, что он _слишком_ фичаст для шелла?
                                  +1
                                  Много — не мало.
                                    +4
                                    да лааадно
                                    image
                                      +2
                                      Это, по-вашему, хорошая аналогия — сравнивать командную строку и графический загромождённый интерфейс? Вы неиспользуемые вами возможности каким суперменским зрением видите в командной строке?
                                  +8
                                  я с PowerShell слишком плохо знаком, чтоб судить о нем. PowerShell наверняка не уступает инструментам мира Unix по возможностям, но дело в том, что линуксовыми тулзами я уже умею пользоваться а PowerShell'у нужно будет учиться. Ну и чисто субъективно мне нравятся концепции лежащие в основе Unix-Way: все потоки данных — это текст, одна задача — одна программа и т.д.
                                    +6
                                    Ну основное отличие в том, что в PowerShell объекты, поэтому возможна запись типа (ls)[0].Name
                                      +1
                                      В PS всё то же самое, только по пайпам объекты идут.
                                      0
                                      Отношение к синтаксису — вещь субъективная. Хоть я и люблю .NET, но PowerShell банально не осилил.
                                        +13
                                        Судя по яростному минусованию некоторые считают неприличным даже спросить, я никак не ожидал.
                                          0
                                          Там всё очень просто, что там можно не осилить?
                                            0
                                            Я и не говорю, что сложно — банально не понравилось.
                                      +2
                                      Ну, хотя бы тем, что автокомплит в нём такой же дебильный, как и в cmd. Хоть он там и расширяемый, но такого же поведения, как в баше, добиться невозможно.
                                        +4
                                        Пустой скрипт стартует 0.4 секунды. Например, команда
                                        powerShell.exe "123"
                                        
                                        На любой современной машине, в 64-битной оси.

                                        Есть немало случаев, когда такое неприемлемо. Например, пользовательские шаги построения проектов в Visual Studio, когда в солюшне десятки проектов. Visual Studio обычно строит проект быстрее чем за секунду (C++ или C#).
                                          +2
                                          PowerShell это в первую очередь shell. Эмулятор терминала там очень незначительно отличается от cmd.
                                            0
                                            PowerShell же язык, а не функциональность самой консоли! Все её проблемы, такие как ненормальный копи-паст, автокомплит и т.п. остаются.
                                            0
                                            Есть ещё Windows Services for UNIX от Microsoft.
                                              +2
                                              Увы, MS объявило его устаревшим и поддерживать не будет. Да и утилиты в комплекте там весьма далеки от современного дистрибутива linux.
                                                0
                                                Порты от бсди в порядке.
                                                +1
                                                О, Боже. Смотрите не поставьте это на РЕАЛЬНЫЙ сервис — если Вы читали README то должны понимать что установка этого пакета необратима + входит в конфликт со стандартным Windows окружением, ну и заодно пути для NTFS станут Case-Sentensive.
                                                0
                                                А на счет буфера обмена не пробовали пользоваться Shift+Insert?
                                                  +2
                                                  Пробовали. Не работает
                                                    –4
                                                    ну в родной виндовой консоли работает, а у вас написано обратное. а в остальном я просто запускаю bash.exe
                                                      +1
                                                      Специально сейчас проверил под Server 2008, под XP и под Server 2003. Просто cmd.exe

                                                      Не работает.

                                                      Пожалуйста, попробуйте тоже на какой-нибудь свежеустановленной (виртуальной) машине. Затем объясните нам, как Вы получили такую ценную фичу.
                                                        +2
                                                        И правда не работает, видимо большую часть времени сижу в консоли FAR а там работает, что то не подумал что это фишка FAR.
                                                        Без мышки будет работать только нажать Alt-Spase выбрать там Edit и Paste
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      не работает, а очень бы хотелось
                                                      +1
                                                        +1
                                                        Сам пользуюсь GnuWin. А есть ли какие-то преимущества у UnxUtils?
                                                        0
                                                        Сейчас cygwin стал очень хорошим и терминал в комплекте нормальный есть и некоторая интеграция с консольным виндовым софтом.
                                                          0
                                                          Пакетный менеджер у него неудобен весьма — ведь в этой роли у него инсталлятор, в mingw гораздо лучше.
                                                            +3
                                                            Если вам не нравится графический менеджер, то есть cyg-apt.
                                                          0
                                                          Для себя использую связку ConEmu + PATH, содержащий путь к гиту (установлен у многих разработчиков и дистрибутив содержит множество Unix-утилит).
                                                            0
                                                            А unix'овские утилиты с выводом от windows'ских справляются?

                                                            cygwin:
                                                            ipconfig /all | grep IP

                                                            портит кодировку
                                                              +6
                                                              С выводом от windows'ских утилит не справляются даже другие windows'ские, потому что часть работает в 866, а часть — в 1251.

                                                              А для unix'овских можно iconv использовать.
                                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                                  –3
                                                                  Не выдумывайте, вывод идет в той кодировке, что указана. У меня это утф8 например.
                                                                    +1
                                                                    Указана кем?
                                                                    Попытка использования chcp
                                                                    Вы знаете какой-то способ указать команде ipconfig кодировку?
                                                                      0
                                                                      Там ниже уже разобрались что в русской винде в части утилит текст захардкожен. Ну что я могу сказать? Не пользуйтесь локализациями, лол.
                                                                        0
                                                                        Кстати, как правильно «захардкожен» или «захардкоден»?
                                                                        0
                                                                        Понекрофилю тут, чтобы отобразилось правильно надо переключить в свойствах консоли шрифт с точечного на нормальный TrueType (при особом желании можно сделать прямо из командной строки, не используя диалог настроек).
                                                                    –1
                                                                    Поставте утф8 везде и проблем не будет.
                                                                      0
                                                                      Где и как изменить кодировку, я так и не понял.
                                                                  0
                                                                  Спасибо автору за статью. Одно время сам хотел что то похожее написать, но руки не доходили )
                                                                    0
                                                                    У clink есть какие-то преимущества перед TCC/LE, кроме опенсорсности?
                                                                      0
                                                                      Предпочитаю Mintty+MSYS+MinGW. По-моему, неплохое сочетание — обновляется, есть пакетный менеджер. Git, увы, только отдельно придется ставить.
                                                                        +1
                                                                        На крайний случай и busybox сгодится.
                                                                          0
                                                                          Гм, а Git Bash рассматривали? из того что я юзаю основных команд там только find не было и он быстро ставится как mingw пакет.
                                                                            0
                                                                            Первая запись в хелпе С-@: set-mark
                                                                            Поясните пожалуйста, что в данном контексте значит @?

                                                                            Правильно ли я ожидаю что, эта комбинация должна будет переключить коммандную строку в режим выделения текста?
                                                                              +2
                                                                              У меня MinGW (тот что с Git-ом идет) и mintty.exe (более-менее правильный терминал, можно даже мышкой менять размер окна), а еще в стартовом скрипте я вызываю chcp.com 65001 (и в настройках терминала стоит utf-8). Короче почти все что нужно работает как надо.

                                                                              И вот я решил проверить как работает ipconfig /all | grep IP
                                                                              Проблему со слешем можно обойти минусом (ipconfig -all | grep IP) а кодировка должна была отработать правильно.

                                                                              Но однако оказалось, что ipconfig даже в родной консоли работает только в кодировке 866, а в 65001 не работает.

                                                                              Бу-Га-Га, 20-летняя ось, а тупит как курсовик второкурсника!
                                                                                –1
                                                                                Откуда в ipconfig 866?
                                                                                  +1
                                                                                  Вот фрагмент hexdump-а того, что выдал ipconfig -all > tmp.txt

                                                                                  0140  74 20 61 64 61 70 74 65  72 20 91 a5 e2 a5 a2 ae  t adapte r Сетево
                                                                                  0150  a5 20 af ae a4 aa ab ee  e7 a5 ad a8 a5 20 42 6c  е подклю чение Bl
                                                                                  0160  75 65 74 6f 6f 74 68 3a  0d 0a 0d 0a 20 20 20 4d  uetooth: ....   M
                                                                                  

                                                                                  Первый символ в третей строке русская е и у нее код А5. Это кодировка cp866.
                                                                                  ipconfig -all > tmp.txt вызывался из штатной консоли cmd.exe сразу после вызова chcp 65001
                                                                                    –3
                                                                                    Клёва! Ни разу в жизни не видел русскую винду…
                                                                                0
                                                                                Даже вроде бы работает в связке с ConEmu — очень неплохо. Спасибо за дельную утилиту.
                                                                                  +3
                                                                                  Чтоб вставить текст из буфера обмена нужно тянуться к мышке
                                                                                  А я уже на автомате жму Alt-пробел, E, P. Намного быстрее выходит, чем мышью.
                                                                                    0
                                                                                    пользуюсь git Bash
                                                                                      +1
                                                                                      Я пользуюсь MobaXTerm. Работает как терминал, ssh-клиент, X-сервер и имеет много встроенных утилит и плагинов.
                                                                                        0
                                                                                        А rm -rf C:\ сработает? А прозрачный фон сделать можно? :)
                                                                                          0
                                                                                          Я бы воздаржался от перезаписывания в папке System32 тем более что есть лучше вариант.
                                                                                          Просто добавьте к переменной PATH путь к распакованым утилитам.
                                                                                          Из скрипта это делается так PATH=%PATH%;«c:\unixutills\bin»
                                                                                            0
                                                                                            Хм, о каком скрипте речь?
                                                                                            Если о cmd, то там set в начале не хватает
                                                                                            А для bash проценты выглядят чужеродно
                                                                                              0
                                                                                              да я имею ввиду cmd. Да нужен set для скрипта.
                                                                                            0
                                                                                            А умеет ли что-нибудь из всего этого работать с ssh по unix-way? Т.е. чтобы не надо было всякие окошки для создания сессии юзать, а можно было набрать «ssh user@server.com -p 2323» скажем. И чтобы можно было задать .ssh/config как в линуксах?
                                                                                              +2
                                                                                              cygwin

                                                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                            Самое читаемое