Как определить, была ли введена команда в командную строку

Как определить, была ли введена команда в командную строку

Я хочу написать, PROMPT_COMMANDкоторый реагирует на то, что было предоставлено в командной строке непосредственно перед этим. Например, чтобы переключаться между развернутым, информативным приглашением или простым, компактным приглашением, например:

mikemol@serenity ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$
mikemol@serenity ~ $

Значения добавляются в историю оболочки только если они не пустые, поэтому я не могу просто проверить, является ли последняя запись истории пустой. Запуск set | grep some_commandпосле запуска some_commandничего мне не дает, поэтому, похоже, нет переменной окружения, содержащей эту информацию.

Я в основном использую bash, но мне было бы интересно узнать о POSIX-совместимых решениях и других оболочках.

решение1

В конечном итоге мне вообще ничего не понадобилось PROMPT_COMMAND. Спасибо Кристоферу за то, что указал мне правильное направление.

Вместо этого рассмотрим этот файл ps1.prompt:

${__cmdnbary[\#]+$(
    echo '\u@\h: \w' # Your fancy prompt goes here, with all the usual special characters available.
) }${__cmdnbary[\#]=}\$

Затем я могу ввести это в свой PS1:

PS1=$(cat ps1.prompt)

(Это не обязательно, но мне показалось удобным для иллюстрирования и редактирования.)

И вот мы видим:

mikemol@zoe:~$ echo hi
hi
mikemol@zoe:~$ echo ho
ho
mikemol@zoe:~$ echo hum
hum
mikemol@zoe:~$ 
mikemol@zoe:~$ PS1=$(cat ps1.prompt)
$ 
mikemol@zoe: ~ $ echo hi
hi
$ echo ho
ho
$ echo hum
hum
$ 
mikemol@zoe: ~ $ 

Мы используем хак массивапродемонстрировано здесь, но вместо использования подстановки bashпараметров ${parameter:-word}мы используем , ${parameter+word}поэтому срабатываем только в том случае, если ранее не выполнялась команда.

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

Как ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}работает

В оригинальной демонстрации хака массива использовалась конструкция ${__cmdnbary[\#]-word}${__cmdnbary[\#]=}. (Я заменил $?на wordдля ясности). Если вы не особо знакомы с расширением параметров и массивами (а я не был), то вообще не ясно, что происходит.

Во-первых, поймите\#

Заруководство:

\#номер команды этой команды

...

Номер команды — это позиция в последовательности команд, выполняемых во время текущего сеанса оболочки.

Это означает, \#что изменится толькоесли и только есливыполняется команда. Если пользователь вводит пустую строку в приглашении, команда не выполняется и не \#изменяется.

Установка пустой строки в ${__cmdnbary[#]=}

${__cmdnbary[\#]=}использует расширение параметра. Возвращаясь круководство:

${parameter:=word}

Назначить значения по умолчанию. Если параметр не установлен или равен нулю, параметру присваивается расширение слова. Затем подставляется значение параметра.

Таким образом, если __cmdnbary[\#]не установлено или равно null, эта конструкция назначит пустую строку ( wordв нашем случае — пустая строка), и вся конструкция будет заменена в нашем выводе той же пустой строкой.

__cmdnbary[\#]волявсегдабыть неустановленным или нулевым, когда мы видим его в первый раз, так как # является монотонным — он всегда увеличивается или остается тем же. (То есть, пока не зациклится, вероятно, около 2^31 или 2^63, но есть и другие проблемы, с которыми мы столкнемсядлинный(Прежде чем мы доберемся туда. Недаром я описываю решение как своего рода хак.)

Условное наклонение в${__cmdnbary[\#]-word}

${__cmdnbary[\#]-word}еще один параметр расширения. Из руководства:

${parameter:-word}

Использовать значения по умолчанию. Если параметр не установлен или равен нулю, подставляется расширение слова. В противном случае подставляется значение параметра.

Итак, если запись массива \#равнанеустановленный или нулевой, wordиспользуется на своем месте. Поскольку мы не пытаемся присвоить __cmdnbary[\#](используя ${parameter:=word}подстановку) до тех пор, пока не проверим его, первый раз, когда мы проверяем его для заданного, \#эта позиция в массиве должна оказаться неустановленной.

bashиспользует разреженные массивы

Одно пояснение для тех, кто привык к массивам в стиле C. bashна самом деле используетразреженные массивы; пока вы не назначитечто-нибудьна позицию в массиве, эта позиция не установлена. Пустая строка — это не то же самое, что «null или unset».

Почему мы используем ${__cmdnbary[#]+word}${__cmdnbary[#]=} вместо этого

${__cmdnbary[\#]+word}${__cmdnbary[\#]=}и ${__cmdnbary[#]-слово}${__cmdnbary[#]=} look very siilar. The *only* thing we change between the two constructs can be found in the first portion; we use${параметр:+слово} instead of${параметр:-слово}`.

Помните, что с ${parameter:-word}, wordотображается только если parameterявляется пустым или неустановленным — в нашем случае, только если мыне имеютпока не установим позицию в массиве, чего мы не сделаем, если и только если \#увеличилось, что произойдет только в том случае, если мы только что выполнили команду.

Это означает, что с помощью ${parameter:-word}мы представим только wordесли мыне имеютвыполнил команду, которая является полной противоположностью того, что мы хотим сделать. Поэтому ${parameter:-word}вместо этого мы используем. Опять же, из руководства:

${parameter:+word}

Использовать альтернативное значение. Если параметр равен нулю или не установлен, ничего не подставляется, в противном случае подставляется расширение слова.

Что (к сожалению) требует еще большего понимания двойной отрицательной логики, но вот так.

Сама подсказка

Мы объяснили механизм переключения, но что насчет самого приглашения?

Здесь я использую, $( ... )чтобы содержать суть приглашения. В первую очередь для моей собственной выгоды для читабельности; вам не обязательно делать это таким образом. Вы можете заменить $( ... )на то, что вы обычно вставляете в назначение переменной.

Почему это взлом?

Помните, как мы добавляем записи в разреженный массив? Мы не удаляем эти записи, и поэтому массив будет вечно расти, пока не будет завершен сеанс оболочки; оболочка просачивается PS1. И насколько я знаю, нет способане установленпеременная или позиция массива в приглашении. Вы можете попробовать in $(), но вы увидите, что это не сработает; изменения, внесенные в пространство имен переменных внутри подоболочки, не будут применены к пространству, из которого была создана подоболочка.

Тымощьпопробуйте использовать его mktmpв начале .bashrc, до PS1назначения, и поместить информацию в полученный файл; тогда вы сможете сравнить текущую информацию \#с тем, что там сохранено, но теперь вы сделаете приглашение зависимым от дискового ввода-вывода, что является хорошим способом заблокировать себя в чрезвычайных ситуациях.

Связанный контент