mirror of
git://git.code.sf.net/p/zsh/code
synced 2024-05-28 10:56:08 +02:00
44435: Handling digita arguments for :h and :t.
Pick number of leading or trailing path components to substitute. Active in history, brace parameters, glob qualifiers. Add tests for all three environments.
This commit is contained in:
parent
80aa807a61
commit
b8dc5a7f6d
|
@ -1,3 +1,10 @@
|
||||||
|
2019-06-20 Peter Stephenson <p.stephenson@samsung.com>
|
||||||
|
|
||||||
|
* 44435: Doc/Zsh/expn.yo, NEWS, README, Src/Zle/compctl.c,
|
||||||
|
Src/glob.c, Src/hist.c, Src/subst.c, Test/D02glob.ztst,
|
||||||
|
Test/D04parameter.ztst, Test/W01history.ztst: Handle
|
||||||
|
trailing digit arguments of :t and :h modifiers.
|
||||||
|
|
||||||
2019-06-19 Peter Stephenson <p.stephenson@samsung.com>
|
2019-06-19 Peter Stephenson <p.stephenson@samsung.com>
|
||||||
|
|
||||||
* Roman Perepelitsa: 44430: Src/prompt.c: various problems with
|
* Roman Perepelitsa: 44430: Src/prompt.c: various problems with
|
||||||
|
|
|
@ -260,9 +260,23 @@ see the definition of the filename extension in the description of the
|
||||||
tt(r) modifier below. Note that according to that definition the result
|
tt(r) modifier below. Note that according to that definition the result
|
||||||
will be empty if the string ends with a `tt(.)'.
|
will be empty if the string ends with a `tt(.)'.
|
||||||
)
|
)
|
||||||
item(tt(h))(
|
item(tt(h) [ var(digits) ])(
|
||||||
Remove a trailing pathname component, leaving the head. This works
|
Remove a trailing pathname component, shortening the path by one
|
||||||
like `tt(dirname)'.
|
directory level: this is the `head' of the pathname. This works like
|
||||||
|
`tt(dirname)'. If the tt(h) is followed immediately (with no spaces or
|
||||||
|
other separator) by any number of decimal digits, and the value of the
|
||||||
|
resulting number is non-zero, that number of leading components is
|
||||||
|
preserved instead of the final component being removed. In an
|
||||||
|
absolute path the leading `tt(/)' is the first component, so,
|
||||||
|
for example, if tt(var=/my/path/to/something), then tt(${var:h3})
|
||||||
|
substitutes tt(/my/path). Consecutive `/'s are treated the same as
|
||||||
|
a single `/'. In parameter substitution, digits may only be
|
||||||
|
used if the expression is in braces, so for example the short form
|
||||||
|
substitution tt($var:h2) is treated as tt(${var:h}2), not as
|
||||||
|
tt(${var:h2}). No restriction applies to the use of digits in history
|
||||||
|
substitution or globbing qualifiers. If more components are requested
|
||||||
|
than are present, the entire path is substituted (so this does not
|
||||||
|
trigger a `failed modifier' error in history expansion).
|
||||||
)
|
)
|
||||||
item(tt(l))(
|
item(tt(l))(
|
||||||
Convert the words to all lowercase.
|
Convert the words to all lowercase.
|
||||||
|
@ -316,9 +330,12 @@ immediately by a tt(g). In parameter expansion the tt(&) must appear
|
||||||
inside braces, and in filename generation it must be quoted with a
|
inside braces, and in filename generation it must be quoted with a
|
||||||
backslash.
|
backslash.
|
||||||
)
|
)
|
||||||
item(tt(t))(
|
item(tt(t) [ var(digits) ])(
|
||||||
Remove all leading pathname components, leaving the tail. This works
|
Remove all leading pathname components, leaving the final component (tail).
|
||||||
like `tt(basename)'.
|
This works like `tt(basename)'. Any trailing slashes are first removed.
|
||||||
|
Decimal digits are handled as described above for (h), but in this
|
||||||
|
case that number of trailing components is preserved instead of
|
||||||
|
the default 1; 0 is treated the same as 1.
|
||||||
)
|
)
|
||||||
item(tt(u))(
|
item(tt(u))(
|
||||||
Convert the words to all uppercase.
|
Convert the words to all uppercase.
|
||||||
|
|
6
NEWS
6
NEWS
|
@ -26,6 +26,12 @@ specify the order of completion matches. This affects the display
|
||||||
of candidate matches and the order in which they are selected when
|
of candidate matches and the order in which they are selected when
|
||||||
cycling between them using menu completion.
|
cycling between them using menu completion.
|
||||||
|
|
||||||
|
The :h and :t modifiers in parameter expansion (if braces are present),
|
||||||
|
glob qualifiers and history expansion may take following decimal digit
|
||||||
|
arguments in order to keep that many leading or trailing path components
|
||||||
|
instead of the defaults of all but one (:h) and one (:t). In an absolute
|
||||||
|
path the leading '/' counts as one component.
|
||||||
|
|
||||||
Changes from 5.6.2 to 5.7.1
|
Changes from 5.6.2 to 5.7.1
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
21
README
21
README
|
@ -30,9 +30,28 @@ Zsh is a shell with lots of features. For a list of some of these, see the
|
||||||
file FEATURES, and for the latest changes see NEWS. For more
|
file FEATURES, and for the latest changes see NEWS. For more
|
||||||
details, see the documentation.
|
details, see the documentation.
|
||||||
|
|
||||||
Incompatibilities since 5.6.2
|
Incompatibilities since 5.7.1
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
The history expansion !:1:t2 used to be interpreted such that the 2
|
||||||
|
was a separate character added after the history expansion. Now
|
||||||
|
it is an argument to the :t modifier.
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
% echo /my/interesting/path
|
||||||
|
% echo !:1:t2
|
||||||
|
|
||||||
|
used to echo "path2", but now echoes "interesting/path".
|
||||||
|
|
||||||
|
The behaviour of :h has similarly changed.
|
||||||
|
|
||||||
|
The behaviour has also changed in forms such as ${foo:t2) and *(:t2),
|
||||||
|
but in those cases the previous behaviour was not meaningful.
|
||||||
|
|
||||||
|
Incompatibilities between 5.6.2 and 5.7.1
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
1) vcs_info git: The gen-unapplied-string hook receives the patches in
|
1) vcs_info git: The gen-unapplied-string hook receives the patches in
|
||||||
order (next to be applied first). This is consistent with the hg
|
order (next to be applied first). This is consistent with the hg
|
||||||
backend and with one of two contradictory claims in the documentation
|
backend and with one of two contradictory claims in the documentation
|
||||||
|
|
|
@ -2511,7 +2511,7 @@ makecomplistcmd(char *os, int incmd, int flags)
|
||||||
else if (!(cmdstr &&
|
else if (!(cmdstr &&
|
||||||
(((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
|
(((ccp = (Compctlp) compctltab->getnode(compctltab, cmdstr)) &&
|
||||||
(cc = ccp->cc)) ||
|
(cc = ccp->cc)) ||
|
||||||
((s = dupstring(cmdstr)) && remlpaths(&s) &&
|
((s = dupstring(cmdstr)) && remlpaths(&s, 1) &&
|
||||||
(ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
|
(ccp = (Compctlp) compctltab->getnode(compctltab, s)) &&
|
||||||
(cc = ccp->cc))))) {
|
(cc = ccp->cc))))) {
|
||||||
if (flags & CFN_DEFAULT)
|
if (flags & CFN_DEFAULT)
|
||||||
|
|
|
@ -400,7 +400,7 @@ insert(char *s, int checked)
|
||||||
if (colonmod) {
|
if (colonmod) {
|
||||||
/* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */
|
/* Handle the remainder of the qualifier: e.g. (:r:s/foo/bar/). */
|
||||||
char *mod = colonmod;
|
char *mod = colonmod;
|
||||||
modify(&news, &mod);
|
modify(&news, &mod, 1);
|
||||||
}
|
}
|
||||||
if (!statted && (gf_sorts & GS_NORMAL)) {
|
if (!statted && (gf_sorts & GS_NORMAL)) {
|
||||||
statfullpath(s, &buf, 1);
|
statfullpath(s, &buf, 1);
|
||||||
|
|
92
Src/hist.c
92
Src/hist.c
|
@ -555,6 +555,27 @@ substfailed(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a count given by decimal digits after a modifier.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
digitcount(void)
|
||||||
|
{
|
||||||
|
int c = ingetc(), count;
|
||||||
|
|
||||||
|
if (idigit(c)) {
|
||||||
|
count = 0;
|
||||||
|
do {
|
||||||
|
count = 10 * count + (c - '0');
|
||||||
|
c = ingetc();
|
||||||
|
} while (idigit(c));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
count = 0;
|
||||||
|
inungetc(c);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* Perform history substitution, returning the next character afterwards. */
|
/* Perform history substitution, returning the next character afterwards. */
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
|
@ -835,7 +856,7 @@ histsubchar(int c)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
if (!remtpath(&sline)) {
|
if (!remtpath(&sline, digitcount())) {
|
||||||
herrflush();
|
herrflush();
|
||||||
zerr("modifier failed: h");
|
zerr("modifier failed: h");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -856,7 +877,7 @@ histsubchar(int c)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
if (!remlpaths(&sline)) {
|
if (!remlpaths(&sline, digitcount())) {
|
||||||
herrflush();
|
herrflush();
|
||||||
zerr("modifier failed: t");
|
zerr("modifier failed: t");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1974,16 +1995,18 @@ chrealpath(char **junkptr)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
int
|
int
|
||||||
remtpath(char **junkptr)
|
remtpath(char **junkptr, int count)
|
||||||
{
|
{
|
||||||
char *str = strend(*junkptr);
|
char *str = strend(*junkptr);
|
||||||
|
|
||||||
/* ignore trailing slashes */
|
/* ignore trailing slashes */
|
||||||
while (str >= *junkptr && IS_DIRSEP(*str))
|
while (str >= *junkptr && IS_DIRSEP(*str))
|
||||||
--str;
|
--str;
|
||||||
/* skip filename */
|
if (!count) {
|
||||||
while (str >= *junkptr && !IS_DIRSEP(*str))
|
/* skip filename */
|
||||||
--str;
|
while (str >= *junkptr && !IS_DIRSEP(*str))
|
||||||
|
--str;
|
||||||
|
}
|
||||||
if (str < *junkptr) {
|
if (str < *junkptr) {
|
||||||
if (IS_DIRSEP(**junkptr))
|
if (IS_DIRSEP(**junkptr))
|
||||||
*junkptr = dupstring ("/");
|
*junkptr = dupstring ("/");
|
||||||
|
@ -1992,6 +2015,34 @@ remtpath(char **junkptr)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Return this many components, so start from the front.
|
||||||
|
* Leading slash counts as one component, consistent with
|
||||||
|
* behaviour of repeated applications of :h.
|
||||||
|
*/
|
||||||
|
char *strp = *junkptr;
|
||||||
|
while (strp < str) {
|
||||||
|
if (IS_DIRSEP(*strp)) {
|
||||||
|
if (--count <= 0) {
|
||||||
|
if (strp == *junkptr)
|
||||||
|
++strp;
|
||||||
|
*strp = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* Count consecutive separators as one */
|
||||||
|
while (IS_DIRSEP(strp[1]))
|
||||||
|
++strp;
|
||||||
|
}
|
||||||
|
++strp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full string needed */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* repeated slashes are considered like a single slash */
|
/* repeated slashes are considered like a single slash */
|
||||||
while (str > *junkptr && IS_DIRSEP(str[-1]))
|
while (str > *junkptr && IS_DIRSEP(str[-1]))
|
||||||
--str;
|
--str;
|
||||||
|
@ -2040,7 +2091,7 @@ rembutext(char **junkptr)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
mod_export int
|
mod_export int
|
||||||
remlpaths(char **junkptr)
|
remlpaths(char **junkptr, int count)
|
||||||
{
|
{
|
||||||
char *str = strend(*junkptr);
|
char *str = strend(*junkptr);
|
||||||
|
|
||||||
|
@ -2050,12 +2101,29 @@ remlpaths(char **junkptr)
|
||||||
--str;
|
--str;
|
||||||
str[1] = '\0';
|
str[1] = '\0';
|
||||||
}
|
}
|
||||||
for (; str >= *junkptr; --str)
|
for (;;) {
|
||||||
if (IS_DIRSEP(*str)) {
|
for (; str >= *junkptr; --str) {
|
||||||
*str = '\0';
|
if (IS_DIRSEP(*str)) {
|
||||||
*junkptr = dupstring(str + 1);
|
if (--count > 0) {
|
||||||
return 1;
|
if (str > *junkptr) {
|
||||||
|
--str;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Whole string needed */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*str = '\0';
|
||||||
|
*junkptr = dupstring(str + 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
/* Count consecutive separators as 1 */
|
||||||
|
while (str >= *junkptr && IS_DIRSEP(*str))
|
||||||
|
--str;
|
||||||
|
if (str <= *junkptr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
37
Src/subst.c
37
Src/subst.c
|
@ -3438,7 +3438,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
|
||||||
s--;
|
s--;
|
||||||
if (unset(KSHARRAYS) || inbrace) {
|
if (unset(KSHARRAYS) || inbrace) {
|
||||||
if (!isarr)
|
if (!isarr)
|
||||||
modify(&val, &s);
|
modify(&val, &s, inbrace);
|
||||||
else {
|
else {
|
||||||
char *ss;
|
char *ss;
|
||||||
char **ap = aval;
|
char **ap = aval;
|
||||||
|
@ -3447,12 +3447,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
|
||||||
|
|
||||||
while ((*pp = *ap++)) {
|
while ((*pp = *ap++)) {
|
||||||
ss = s;
|
ss = s;
|
||||||
modify(pp++, &ss);
|
modify(pp++, &ss, inbrace);
|
||||||
}
|
}
|
||||||
if (pp == aval) {
|
if (pp == aval) {
|
||||||
char *t = "";
|
char *t = "";
|
||||||
ss = s;
|
ss = s;
|
||||||
modify(&t, &ss);
|
modify(&t, &ss, inbrace);
|
||||||
}
|
}
|
||||||
s = ss;
|
s = ss;
|
||||||
}
|
}
|
||||||
|
@ -4182,6 +4182,12 @@ arithsubst(char *a, char **bptr, char *rest)
|
||||||
* PTR is an in/out parameter. On entry it contains the string of colon
|
* PTR is an in/out parameter. On entry it contains the string of colon
|
||||||
* modifiers. On return it points past the last recognised modifier.
|
* modifiers. On return it points past the last recognised modifier.
|
||||||
*
|
*
|
||||||
|
* INBRACE is non-zero if we are in some form of a bracketed or
|
||||||
|
* parenthesised expression; it is zero for modifiers ocurring
|
||||||
|
* in an an unbracketed variable substitution. This means that
|
||||||
|
* $foo:t222 is treated ias ${foo:t}222 rather than ${foo:t222}
|
||||||
|
* for backward compatibility.
|
||||||
|
*
|
||||||
* Example:
|
* Example:
|
||||||
* ENTRY: *str is "." *ptr is ":AN"
|
* ENTRY: *str is "." *ptr is ":AN"
|
||||||
* RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N"
|
* RETURN: *str is "/home/foobar" (equal to $PWD) *ptr points to the "N"
|
||||||
|
@ -4189,7 +4195,7 @@ arithsubst(char *a, char **bptr, char *rest)
|
||||||
|
|
||||||
/**/
|
/**/
|
||||||
void
|
void
|
||||||
modify(char **str, char **ptr)
|
modify(char **str, char **ptr, int inbrace)
|
||||||
{
|
{
|
||||||
char *ptr1, *ptr2, *ptr3, *lptr, c, *test, *sep, *t, *tt, tc, *e;
|
char *ptr1, *ptr2, *ptr3, *lptr, c, *test, *sep, *t, *tt, tc, *e;
|
||||||
char *copy, *all, *tmp, sav, sav1, *ptr1end;
|
char *copy, *all, *tmp, sav, sav1, *ptr1end;
|
||||||
|
@ -4202,6 +4208,8 @@ modify(char **str, char **ptr)
|
||||||
*str = dupstring(*str);
|
*str = dupstring(*str);
|
||||||
|
|
||||||
while (**ptr == ':') {
|
while (**ptr == ':') {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
lptr = *ptr;
|
lptr = *ptr;
|
||||||
(*ptr)++;
|
(*ptr)++;
|
||||||
wall = gbal = 0;
|
wall = gbal = 0;
|
||||||
|
@ -4214,10 +4222,8 @@ modify(char **str, char **ptr)
|
||||||
case 'a':
|
case 'a':
|
||||||
case 'A':
|
case 'A':
|
||||||
case 'c':
|
case 'c':
|
||||||
case 'h':
|
|
||||||
case 'r':
|
case 'r':
|
||||||
case 'e':
|
case 'e':
|
||||||
case 't':
|
|
||||||
case 'l':
|
case 'l':
|
||||||
case 'u':
|
case 'u':
|
||||||
case 'q':
|
case 'q':
|
||||||
|
@ -4226,6 +4232,17 @@ modify(char **str, char **ptr)
|
||||||
c = **ptr;
|
c = **ptr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
case 't':
|
||||||
|
c = **ptr;
|
||||||
|
if (inbrace && idigit((*ptr)[1])) {
|
||||||
|
do {
|
||||||
|
count = 10 * count + ((*ptr)[1] - '0');
|
||||||
|
++(*ptr);
|
||||||
|
} while (idigit((*ptr)[1]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
c = **ptr;
|
c = **ptr;
|
||||||
(*ptr)++;
|
(*ptr)++;
|
||||||
|
@ -4392,7 +4409,7 @@ modify(char **str, char **ptr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'h':
|
case 'h':
|
||||||
remtpath(©);
|
remtpath(©, count);
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
remtext(©);
|
remtext(©);
|
||||||
|
@ -4401,7 +4418,7 @@ modify(char **str, char **ptr)
|
||||||
rembutext(©);
|
rembutext(©);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
remlpaths(©);
|
remlpaths(©, count);
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
copy = casemodify(tt, CASMOD_LOWER);
|
copy = casemodify(tt, CASMOD_LOWER);
|
||||||
|
@ -4478,7 +4495,7 @@ modify(char **str, char **ptr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'h':
|
case 'h':
|
||||||
remtpath(str);
|
remtpath(str, count);
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
remtext(str);
|
remtext(str);
|
||||||
|
@ -4487,7 +4504,7 @@ modify(char **str, char **ptr)
|
||||||
rembutext(str);
|
rembutext(str);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
remlpaths(str);
|
remlpaths(str, count);
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
*str = casemodify(*str, CASMOD_LOWER);
|
*str = casemodify(*str, CASMOD_LOWER);
|
||||||
|
|
|
@ -700,3 +700,31 @@
|
||||||
print ${value//[${foo}b-z]/x}
|
print ${value//[${foo}b-z]/x}
|
||||||
0:handling of - range in complicated pattern context
|
0:handling of - range in complicated pattern context
|
||||||
>xx
|
>xx
|
||||||
|
|
||||||
|
pathtotest=glob.tmp/my/test/dir/that/does/not/exist
|
||||||
|
mkdir -p $pathtotest
|
||||||
|
print $pathtotest(:h)
|
||||||
|
print $pathtotest(:h0)
|
||||||
|
print $pathtotest(:h10)
|
||||||
|
print $pathtotest(:h3)
|
||||||
|
print $pathtotest(:h2)
|
||||||
|
print $pathtotest(:h1)
|
||||||
|
print $pathtotest(:t)
|
||||||
|
print $pathtotest(:t0)
|
||||||
|
print $pathtotest(:t10)
|
||||||
|
print $pathtotest(:t3)
|
||||||
|
print $pathtotest(:t2)
|
||||||
|
print $pathtotest(:t1)
|
||||||
|
0:modifiers :h and :t with numbers (main test is in D04parameter.ztst)
|
||||||
|
>glob.tmp/my/test/dir/that/does/not
|
||||||
|
>glob.tmp/my/test/dir/that/does/not
|
||||||
|
>glob.tmp/my/test/dir/that/does/not/exist
|
||||||
|
>glob.tmp/my/test
|
||||||
|
>glob.tmp/my
|
||||||
|
>glob.tmp
|
||||||
|
>exist
|
||||||
|
>exist
|
||||||
|
>glob.tmp/my/test/dir/that/does/not/exist
|
||||||
|
>does/not/exist
|
||||||
|
>not/exist
|
||||||
|
>exist
|
||||||
|
|
|
@ -2445,3 +2445,80 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888
|
||||||
: <<< ${(F)x/y}
|
: <<< ${(F)x/y}
|
||||||
}
|
}
|
||||||
0:Separation / join logic regresssion test
|
0:Separation / join logic regresssion test
|
||||||
|
|
||||||
|
testpath=/one/two/three/four
|
||||||
|
for (( i = 0; i <= 6; ++i )); do
|
||||||
|
eval "print \$testpath:t$i"
|
||||||
|
eval "print \${testpath:t$i}"
|
||||||
|
done
|
||||||
|
0:t with trailing digits
|
||||||
|
>four0
|
||||||
|
>four
|
||||||
|
>four1
|
||||||
|
>four
|
||||||
|
>four2
|
||||||
|
>three/four
|
||||||
|
>four3
|
||||||
|
>two/three/four
|
||||||
|
>four4
|
||||||
|
>one/two/three/four
|
||||||
|
>four5
|
||||||
|
>/one/two/three/four
|
||||||
|
>four6
|
||||||
|
>/one/two/three/four
|
||||||
|
|
||||||
|
testpath=/one/two/three/four
|
||||||
|
for (( i = 0; i <= 6; ++i )); do
|
||||||
|
eval "print \$testpath:h$i"
|
||||||
|
eval "print \${testpath:h$i}"
|
||||||
|
done
|
||||||
|
0:h with trailing digits
|
||||||
|
>/one/two/three0
|
||||||
|
>/one/two/three
|
||||||
|
>/one/two/three1
|
||||||
|
>/
|
||||||
|
>/one/two/three2
|
||||||
|
>/one
|
||||||
|
>/one/two/three3
|
||||||
|
>/one/two
|
||||||
|
>/one/two/three4
|
||||||
|
>/one/two/three
|
||||||
|
>/one/two/three5
|
||||||
|
>/one/two/three/four
|
||||||
|
>/one/two/three6
|
||||||
|
>/one/two/three/four
|
||||||
|
|
||||||
|
testpath=/a/quite/long/path/to/do/messy/stuff/with
|
||||||
|
print $testpath:h2:t3:h5:t16:h2n2
|
||||||
|
print ${testpath:t5:h2}
|
||||||
|
print ${testpath:t5:h2:t}
|
||||||
|
print ${testpath:h6:t4:h3:t2:h}
|
||||||
|
print ${testpath:h10:t10:t6:h3}
|
||||||
|
print ${testpath:t9:h}
|
||||||
|
print ${testpath:t9:h:t}
|
||||||
|
0:Combinations of :h and :t with and without trailing digits
|
||||||
|
>/a/quite/long/path/to/do/messy/stuff2:t3:h5:t16:h2n2
|
||||||
|
>to/do
|
||||||
|
>do
|
||||||
|
>long
|
||||||
|
>path/to/do
|
||||||
|
>a/quite/long/path/to/do/messy/stuff
|
||||||
|
>stuff
|
||||||
|
|
||||||
|
testpath=///this//has////lots//and////lots//of////slashes
|
||||||
|
print ${testpath:h3}
|
||||||
|
print ${testpath:t4}
|
||||||
|
0:Multiple slashes are treated as one in :h and :t but are not removed
|
||||||
|
>///this//has
|
||||||
|
>and////lots//of////slashes
|
||||||
|
|
||||||
|
testpath=trailing/slashes/are/removed///
|
||||||
|
print ${testpath:h}
|
||||||
|
print ${testpath:h2}
|
||||||
|
print ${testpath:t}
|
||||||
|
print ${testpath:t2}
|
||||||
|
0:Modifiers :h and :t remove trailing slashes before examining path
|
||||||
|
>trailing/slashes/are
|
||||||
|
>trailing/slashes
|
||||||
|
>removed
|
||||||
|
>are/removed
|
||||||
|
|
|
@ -58,3 +58,26 @@
|
||||||
*?*
|
*?*
|
||||||
F:Check that a history bug introduced by workers/34160 is working again.
|
F:Check that a history bug introduced by workers/34160 is working again.
|
||||||
# Discarded line of error output consumes prompts printed by "zsh -i".
|
# Discarded line of error output consumes prompts printed by "zsh -i".
|
||||||
|
|
||||||
|
$ZTST_testdir/../Src/zsh -fis <<<'
|
||||||
|
echo /my/path/for/testing
|
||||||
|
echo !1:1:h10
|
||||||
|
echo !1:1:h3
|
||||||
|
echo !1:1:h2
|
||||||
|
echo !1:1:h1
|
||||||
|
echo !1:1:t10
|
||||||
|
echo !1:1:t3
|
||||||
|
echo !1:1:t2
|
||||||
|
echo !1:1:t1
|
||||||
|
echo !1:1:t3:h2' 2>/dev/null
|
||||||
|
0:Modifiers :h and :t with arguments
|
||||||
|
>/my/path/for/testing
|
||||||
|
>/my/path/for/testing
|
||||||
|
>/my/path
|
||||||
|
>/my
|
||||||
|
>/
|
||||||
|
>/my/path/for/testing
|
||||||
|
>path/for/testing
|
||||||
|
>for/testing
|
||||||
|
>testing
|
||||||
|
>path/for
|
||||||
|
|
Loading…
Reference in New Issue