Подтвердить что ты не робот

Как распечатать текущую подсказку bash?

Вопрос прост. Я хочу оценить текущее значение PS1 в моем bash script.

Все материалы в google указывают на учебные пособия по сутенерству, но я хочу оценить, как это будет отображаться моим текущим терминалом или, по крайней мере, с помощью какого-либо терминала.

Есть ли какая-нибудь мягкая/функция, которая поможет мне достичь этого? Конечно, я бы хотел, чтобы все экранированные символы были оценены, поэтому echo $PS1 не так полезен в моем случае.

4b9b3361

Ответ 1

Bash 4. Решение 4+ с использованием преобразования параметров для строки приглашения: echo "${[email protected]}"

[[email protected] ~]$ echo "the prompt is '${[email protected]}'"
the prompt is '[[email protected] ~]$'
[[email protected] ~]$ TEST_STRING='\u is dining at \t using \s \V'
[[email protected] ~]$ echo "${TEST_STRING}"
\u is dining at \t using \s \V
[[email protected] ~]$ echo "${[email protected]}"
adamhotep is dining at 21:45:10 using bash 5.0.3
[[email protected] ~]$ 

Со страницы Справочного руководства Bash в Расширении параметров оболочки:

${[email protected]}

Преобразование параметров. Расширение - это преобразование значения параметра или информации о самом параметре в зависимости от значения оператора.
Каждый оператор представляет собой одну букву:

Q    The expansion is a string that is the value of parameter quoted in a
     format that can be reused as input.
E    The expansion is a string that is the value of parameter with backslash
     escape sequences expanded as with the $'...' quoting mechanism.
P    The expansion is a string that is the result of expanding the value of
     parameter as if it were a prompt string (see PROMPTING below).
A    The expansion is a string in the form of an assignment statement or
     declare command that, if evaluated, will recreate parameter with its
     attributes and value.
a    The expansion is a string consisting of flag values representing
     parameter attributes.

Если параметр равен @ или *, операция применяется к каждому позиционному параметру по очереди, и расширение является результирующим списком. Если параметр является переменной массива, подписанной с помощью @ или *, операция применяется к каждому члену массива по очереди, и расширение является результирующим списком.

(См. Также этот ответ из дублирующего вопроса Echo, расширенного PS1.)

Z Shell (zsh) может сделать это с помощью ${(%%)PS1} или с помощью встроенного флага print -P:

[[email protected] ~]% echo "the prompt is '${(%%)PS1}'"
the prompt is '[[email protected] ~]%'
[[email protected] ~]% print -P "the prompt is '$PS1'"
the prompt is '[[email protected] ~]%'
[[email protected] ~]% TEST_STRING="%n is dining at %* using %N $ZSH_VERSION"
[[email protected] ~]% echo "$TEST_STRING"
%n is dining at %* using %N 5.7.1
[[email protected] ~]% echo "${(%%)TEST_STRING}"
adkatz is dining at 11:49:01 using zsh 5.7.1
[[email protected] ~]% print -P "$TEST_STRING"
adkatz is dining at 11:49:07 using zsh 5.7.1
[[email protected] ~]% 

Руководство по расширению и замене Zsh сообщает нам:

Флаги расширения параметров. Если за открывающей скобкой непосредственно следует открывающая скобка, строка до соответствующей закрывающей скобки будет принята как список флагов. В случаях, когда повторение флага имеет смысл, повторения не обязательно должны быть последовательными; например, (q%q%q) означает то же самое, что и более читаемый (%%qqq). Поддерживаются следующие флаги:
...

% Развернуть все % escape в результирующих словах так же, как в приглашениях (см. Расширение подсказок). Если этот флаг задан дважды, полное расширение запроса будет выполнено для результирующих слов, в зависимости от настройки параметров PROMPT_PERCENT, PROMPT_SUBST и PROMPT_BANG.

Из документации Zsh Builtins для print:

-P Выполнить быстрое расширение (см. Быстрое расширение). В сочетании с -f, escape-последовательности подсказок анализируются только в интерполированных аргументах, а не в строке формата.

Ответ 2

Еще одна возможность, используя утилиту script (часть пакета bsdutils на ubuntu):

$ TEST_PS1="\e[31;1m\[email protected]\h:\n\e[0;1m\$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk 'NR==2' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
echo -n $RANDOM_STRING
echo -n $RANDOM_STRING
exit
EOF
<prints the formatted prompt properly here>
Команда

script генерирует указанный файл, а вывод также показан на stdout. Если имя файла опущено, оно генерирует файл с именем typescript.

Поскольку в этом случае нас не интересует файл журнала, имя файла указывается как /dev/null. Вместо этого stdout команды script передается awk для дальнейшей обработки.

  • Весь код также может быть инкапсулирован в функцию.
  • Кроме того, приглашение вывода также может быть назначено переменной.
  • Этот подход также поддерживает синтаксический анализ PROMPT_COMMAND...

EDIT:
Похоже, что новая версия script перекликается с каналом stdin в typescript. Чтобы справиться с этим, вышеупомянутый механизм можно изменить на:

$ TEST_PS1="\e[31;1m\[email protected]\h:\n\e[0;1m\$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk '{old=current; current=$0;} END{print old}' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
alias $RANDOM_STRING=true
$RANDOM_STRING
$RANDOM_STRING
EOF

<prints the formatted prompt properly here>

Объяснение:

Попробуйте ввести эти команды вручную на терминале. Скопируйте эти команды под heredoc, как они есть, и вставьте их средним нажатием мыши. Команда stdout команды script будет содержать что-то очень похожее.

например. В приведенном выше случае вывод команды script дает следующее:

PS1="\e[31;1m\[email protected]\h:\n\e[0;1m$ \e[0m"; HISTFILE=/dev/null
alias some_random_string_here_that_is_not_part_of_PS1=true
some_random_string_here_that_is_not_part_of_PS1
some_random_string_here_that_is_not_part_of_PS1
 \e[0m"; HISTFILE=/dev/nullhsane-dev : ~/Desktop $ PS1="\e[31;1m\[email protected]\h:\n\e[0;1m$ 
[email protected]:
$ alias some_random_string_here_that_is_not_part_of_PS1=true
[email protected]:
$ some_random_string_here_that_is_not_part_of_PS1
[email protected]:
$ some_random_string_here_that_is_not_part_of_PS1
[email protected]:
$ exit

Разделите это stdout с "some_random_string_here_that_is_not_part_of_PS1" как разделитель (разделитель записи awk) и напечатайте последнюю, но одну запись.

EDIT2:

Другой механизм (с использованием bash исходного кода и gdb):

$ gdb -batch -p $$ -ex 'call bind_variable("expanded_PS1", decode_prompt_string (get_string_value ("PS1")), 0)'
$ echo "$expanded_PS1"
<prints the formatted prompt properly here>
  • Здесь есть крошечная проблема. Строки \[ или \] в PS1 будут напечатаны как \1/\2 соответственно. Вы можете удалить их с помощью tr -d '\1\2' <<< "$expanded_PS1"
  • Если вы получили ошибку, например, gdb не удалось подключиться к процессу (похоже, в ubuntu: - \), запустите gdb с помощью sudo.

Ответ 3

Другим способом сделать это будет eval повторение вашего приглашения для обработки любого расширения (не было уверенности в том, почему скобки остаются). Это скорее всего менее надежный, чем метод @anishsane, но может быть немного быстрее:

show-prompt() {
    eval 'echo -en "'$PS1'"' | sed -e 's#\\\[##g' -e 's#\\\]##g'
}
# To show it in a function registered with `complete -F` on
# a single tab, and keep the user input:
show-prompt
echo -n "${COMP_WORDS[@]}"

Взаимодействовал с этим в отношении проблема GitHub.

Ответ 4

Попробуйте выполнить команду

echo $PS1 | 
sed -e s/'\\d'/"$(date +'%a %b %_d')"/g | 
sed -e s/'\\t'/"$(date +'%T')"/g | 
sed -e s/'\\@'/"$(date +'%r')"/g | 
sed -e s/'\\T'/"$(date +'%r'| awk {'print $1'})"/g | 
sed -e s/'\\e'//g | sed -e s/'\\h'/"$HOSTNAME"/g | 
sed -e s/'\\h'/"$HOSTNAME"/g | 
sed -e s/'\\H'/"$HOSTNAME"/g | 
sed -e s/'\\u'/"$USER"/g | 
sed -e [email protected]'\\W'@"$(pwd)"@g | 
sed -e s/'\\w'/"$(pwd | sed -e [email protected][email protected]'~'@g )"/g | 
sed -e s/"\\\\"//g | 
sed -e s/"\\["//g | 
sed -e s/"\\]"/*/g | 
cut -d'*' -f2 | 
cut -d';' -f2 | 
sed s/\ //g | 
sed -e s/[a-z]$/"$([ "$USER" != "root" ] && echo \$ || echo \#)"/g

Ответ 5

Я бы получил это так:

echo $PS1

И затем отредактируйте это с редактором. После этого для теста (это устанавливается, когда сеанс активен):

PS1='\[\033[1m\]\[\033[34m\]\u\[\033[90m\]@\[\033[01;35m\]\h:\[\033[01;32m\]\W\[\033[0m\]$ '

(\ u для пользователя, \h для хоста, \w для полного пути и \W для короткого пути)

И если мне это понравится, я сделаю его постоянным, изменив значение PS1 в ~/.bashrc

PS:

Если вы хотите увидеть все глобальные переменные:

printenv

ИЛИ ЖЕ:

printenv <name_of_var_to_see>

Ответ 6

Отредактируйте файл /etc/bashrc

вы можете использовать это в качестве примера и проверить вывод

    # If id command returns zero, you’ve root access.
if [ $(id -u) -eq 0 ];
then # you are root, set red colour prompt
  PS1="\\[$(tput setaf 1)\\]\\[email protected]\\h:\\w #\\[$(tput sgr0)\\]"
else # normal
  PS1="[\\[email protected]\\h:\\w] $"
fi