
Я хочу написать, 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
назначения, и поместить информацию в полученный файл; тогда вы сможете сравнить текущую информацию \#
с тем, что там сохранено, но теперь вы сделаете приглашение зависимым от дискового ввода-вывода, что является хорошим способом заблокировать себя в чрезвычайных ситуациях.