Щоб отримати той самий результат, який ви зазначаєте у своєму запитанні, все, що потрібно, це:
PS1='${PS2c##*[$((PS2c=0))-9]}- > '
PS2='$((PS2c=PS2c+1)) > '
Вам не потрібно контрувати. Ці два рядки будуть робити це у будь-якій оболонці, яка претендує на що-небудь, близьке до сумісності POSIX.
- > cat <<HD
1 > line 1
2 > line $((PS2c-1))
3 > HD
line 1
line 2
- > echo $PS2c
0
Але мені це сподобалось. І я хотів продемонструвати основи того, що робить цю роботу трохи кращою. Тому я це трохи відредагував. Я до цього часу застряг, /tmp
але я думаю, що я збираюся це зберегти і для себе. Це тут:
cat /tmp/prompt
СКРИПТ ПРОМПИ:
ps1() { IFS=/
set -- ${PWD%"${last=${PWD##/*/}}"}
printf "${1+%c/}" "$@"
printf "$last > "
}
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
PS2='$((PS2c=PS2c+1)) > '
Примітка: нещодавно дізнавшись про яш , я побудував його вчора. З будь-якої причини він не друкує перший байт кожного аргументу %c
рядком - хоча документи були специфічними щодо розширень із широкою діаграмою для цього формату, і, можливо, це пов'язано, - але це добре справляється з%.1s
Ось і вся справа. Там відбуваються дві основні речі. І ось як це виглядає:
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 >
ЧАСТИНА $PWD
Кожен раз, коли $PS1
оцінюється, він аналізує та друкує, $PWD
щоб додати до підказки. Але мені не подобається весь $PWD
переповнюючий екран, тому я хочу лише першу букву кожної сухарки в поточному контурі до поточного каталогу, яку я хотів би побачити повністю. Подобається це:
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cd /
/ > cd ~
/h/mikeserv >
Тут є кілька кроків:
IFS=/
нам доведеться розділити поточний $PWD
і найнадійніший спосіб зробити це з $IFS
розділенням /
. Після цього взагалі не потрібно зациклюватися на цьому - все розщеплення звідси і далі буде визначено $@
масивом позиційних параметрів оболонки в наступній команді, наприклад:
set -- ${PWD%"${last=${PWD##/*/}}"}
Отже, це трохи хитро, але головне, що ми розбиваємося $PWD
на /
символи. Я також використовую розширення параметрів, щоб призначити $last
всім після будь-якого значення, що виникає між лівою лівою частиною та правою найбільшою /
косою рисою. У такий спосіб я знаю, що якщо я просто у себе /
і маю лише один, /
то $last
все одно дорівнюватиме цілому $PWD
і $1
буде порожнім. Це має значення. Я також знімаю $last
з хвостового кінця $PWD
перед тим, як призначити його $@
.
printf "${1+%c/}" "$@"
Тож тут - доки ${1+is set}
ми printf
перший %c
характер аргументів кожної своєї оболонки - який ми тільки що встановили до кожного каталогу в нашому поточному $PWD
- за винятком верхнього каталогу - розділилися /
. Таким чином, ми по суті просто друкуємо перший символ у кожному каталозі, $PWD
окрім верхнього. Однак важливо усвідомити, що це відбувається лише тоді, коли $1
він взагалі встановиться, що не відбудеться під коренем /
або коли буде видалено /
таке, як у /etc
.
printf "$last > "
$last
- це змінна, яку я щойно призначив нашому верхньому каталогу. Отже, це наш головний каталог. Він друкує, чи зробив останній вислів чи ні. І це потрібно для акуратної >
мірки.
АЛЕ ЩО ПРО ВПРОВАДЖЕННЯ?
А тут справа $PS2
умовного. Я раніше показав, як це можна зробити, що ви все ще можете знайти нижче - це принципово питання сфери застосування. Але є ще трохи до цього, якщо ви не хочете почати робити купу printf \b
просторів, а потім намагатися врівноважити їх кількість персонажів ... тьфу. Тому я роблю це:
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
Знову ж, ${parameter##expansion}
економить день. Тут трохи дивно - ми фактично встановлюємо змінну, поки знімаємо її. Ми використовуємо його нове значення - встановлену середину смуги - як глобус, з якого ми знімаємо. Розумієш? Ми ##*
знімаємо все від голови нашої змінної приросту до останнього символу, який може бути чим завгодно [$((PS2c=0))-9]
. Ми гарантуємо таким чином, що не виводимо значення, і все-таки ми його призначаємо. Це досить круто - я ніколи цього не робив. Але POSIX також гарантує нам, що це самий портативний спосіб зробити це.
І саме завдяки POSIX вказано, ${parameter} $((expansion))
що ці визначення зберігаються в поточній оболонці, не вимагаючи, щоб ми встановлювали їх в окрему підпакет, незалежно від того, де ми їх оцінюємо. І саме тому він працює в dash
і sh
так само добре, як і в bash
і zsh
. Ми не використовуємо оболонки / термінали, що залежать від оболонок / терміналів, і ми дозволяємо самим змінним перевірити. Ось що робить портативний код швидким.
Решта досить проста - просто приріст нашого лічильника щоразу $PS2
оцінюється, поки $PS1
знову не скине його. Подобається це:
PS2='$((PS2c=PS2c+1)) > '
Тож тепер я можу:
DASH DEMO
ENV=/tmp/prompt dash -i
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 > printf '\t%s\n' "$PS1" "$PS2" "$PS2c"
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
0
/u/s/m/man3 > cd ~
/h/mikeserv >
SH DEMO
Це працює так само в bash
або sh
:
ENV=/tmp/prompt sh -i
/h/mikeserv > cat <<HEREDOC
1 > $( echo $PS2c )
2 > $( echo $PS1 )
3 > $( echo $PS2 )
4 > HEREDOC
4
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
/h/mikeserv > echo $PS2c ; cd /
0
/ > cd /usr/share
/u/share > cd ~
/h/mikeserv > exit
Як я вже говорив вище, головна проблема полягає в тому, що вам потрібно врахувати, де ви робите свої обчислення. Ви не отримуєте стан у батьківській оболонці - значить, ви не обчислюєте там. Ви отримуєте стан в нижній частині - значить, там і обчислюєте. Але ви робите визначення у батьківській оболонці.
ENV=/dev/fd/3 sh -i 3<<\PROMPT
ps1() { printf '$((PS2c=0)) > ' ; }
ps2() { printf '$((PS2c=PS2c+1)) > ' ; }
PS1=$(ps1)
PS2=$(ps2)
PROMPT
0 > cat <<MULTI_LINE
1 > $(echo this will be line 1)
2 > $(echo and this line 2)
3 > $(echo here is line 3)
4 > MULTI_LINE
this will be line 1
and this line 2
here is line 3
0 >
man 1 mktemp
.