Я работаю над пользовательским приглашением ZSH и хочу повторить символ n
раз в строке (например, пробелы для заполнения). Эта строка печатается с print -rP
( -r
флаг игнорирует соглашения об эхо-вызове, а -P
флаг выполняет расширения приглашения).
У меня есть рабочий код, использующий какую-то подстановку строк, но я не знаю, как он работает. По какой-то причине мне приходится умножать количество символов, которые я хочу напечатать, на два, что похоже на хак.
$ n=3
$ c='a'
$ print -rP "${(l:$n::$c:)}" # why doesn't this work?
ca
$ print -rP "${(l:(( $n * 2 ))::$c:)}" # but this does?
aaa
Итак, 1) почему это работает при умножении на два и 2) каков правильный синтаксис для повторения символа в строке?
решение1
1) почему это работает при умножении на два,
Расширение "${(l:3::$c:)}"
расширяется до c$c
тогда как "${(l:3*2::$c:)}"
расширяется до $c$c$c
. Если опция PROMPT_SUBST
установлена и эта строка используется как часть строки приглашения, она оценивается для расширения параметров, подстановки команд и арифметического расширения. Так что если c=a
, то c$c
становится ca
и $c$c$c
становится aaa
.
Тест с XTRACE
набором:
$ n=3 c=a zsh -o PROMPT_SUBST -xc 'print -rP -- "${(l:n::$c:)}"'
+zsh:1> print -rP -- 'c$c'
ca
$ n=3 c=a zsh -o PROMPT_SUBST -xc 'print -rP -- "${(l:n*2::$c:)}"'
+zsh:1> print -rP -- '$c$c$c'
aaa
и 2) каков правильный синтаксис для повторения символа в строке?
Флаг расширения параметра l
можно использовать так же, как вы его уже используете. Однако флаг p
следует использовать, чтобы разрешить $c
использовать аргумент строки в качестве значения переменной c
до заполнения (спасибо @StéphaneChazelas за указание на это).
$ n=3 c=a zsh -xc 'print -r -- "${(pl:n::$c:)}"'
+zsh:1> print -r -- aaa
aaa
Обратите внимание, что это единственная форма расширения параметров, принимаемая этой конструкцией, согласно man zshexpn
(в разделе о флагах расширения параметров):
p
Распознавать те же управляющие последовательности, что иВ качестве альтернативы, с этой опцией строковые аргументы могут быть в форме ,
$var
в этом случае значение переменной подставляется. Обратите внимание, что эта форма строгая; строковый аргумент не подвергается общему расширению параметров.
решение2
Используя вашу оригинальную нотацию, вы можете добиться желаемого, используя p
вначалофлагов параметров
print "${(pl:$n::$c:)}"
Дополнительную информацию и другие полезные примеры см. в разделе5.4.6: Еще больше флагов параметроввГлава 5: Заменыиз руководства zsh. В нем упоминается заглавный регистр P
, но не p
:
Вот еще несколько флагов параметров; я повторяю некоторые из них. Очень полезный — t
сообщить вам тип параметра. Это также было в главе 3. Чаще всего его используют для проверки базового типа параметра перед попыткой его использования:
if [[ ${(t)myparam} != *assoc* ]]; then
# $myparam is not an associative array. Do something about it.
fi
Другой очень полезный тип — для левого или правого заполнения строки до указанной длины и, при необходимости, с указанной строкой заполнения, которая будет использоваться вместо пробела; вы даже можете указать одноразовую строку, которая будет идти сразу за нужной строкой.
foo='abcdefghij'
for (( i = 1; i <= 10; i++ )); do
goo=${foo[1,$i]}
print ${(l:10::X::Y:)goo} ${(r:10::X::Y:)goo}
done
выводит довольно симпатичное:
XXXXXXXXYa aYXXXXXXXX
XXXXXXXYab abYXXXXXXX
XXXXXXYabc abcYXXXXXX
XXXXXYabcd abcdYXXXXX
XXXXYabcde abcdeYXXXX
XXXYabcdef abcdefYXXX
XXYabcdefg abcdefgYXX
XYabcdefgh abcdefghYX
Yabcdefghi abcdefghiY
abcdefghij abcdefghij
Обратите внимание, что эти двоеточия (которые могут быть другими символами, как я объяснил для флагов ( s
) и ( j
) ) всегда встречаются парами до и после аргумента, так что при трех аргументах двоеточия между ними удваиваются. Вы можете пропустить часть :Y:
и :X:
часть и посмотреть, что произойдет. Строки заполнения не обязательно должны быть одиночными символами; если они не помещаются точное количество раз в пространство заполнения, последнее повторение будет усечено на конце, наиболее удаленном от вставляемого аргумента параметра.
Два параметра сообщают оболочке, что вы хотите сделать что-то особенное со значением подстановки параметра. Флаг ( P
) заставляет значение рассматриваться как имя параметра, так что вы получаете эффект двойной подстановки:
% final=string
% intermediate=final
% print ${(P)intermediate}
string
Это немного похоже $intermediate
на то, что в ksh называется a nameref
, параметр, который помечен как ссылка на другой параметр. В Zsh они тоже могут быть; есть места, где они намного удобнее флага (P)
.
Более мощный флаг — ( e
), который заставляет значение повторно сканироваться для всех форм замены одного слова. Например,
% foo='$(print $ZSH_VERSION)'
% print ${(e)foo}
4.0.2
$foo
заставил пересмотреть значение , после чего подстановка команды была найдена и выполнена.
Оставшиеся флаги — это несколько простых специальных приемов форматирования: упорядочить элементы массива в нормальном лексическом (символьном) порядке с помощью ( o
), упорядочить в обратном порядке с помощью ( O
), сделать то же самое независимо от регистра с помощью ( oi
) или ( Oi
) соответственно, расширить экранированные %
подсказки с помощью ( %
) (легко запомнить), расширить экранированные обратные косые черты, как это делает print с помощью p, принудительно сделать все символы заглавными с помощью ( U
) или строчными с помощью ( L
), сделать заглавными первый символ строки или каждый элемент массива с помощью ( C
), отобразить специальные символы как экранированные последовательности с помощью ( V
). Этого должно быть достаточно, чтобы приступить к работе.