-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Related problem
Motivating example:
Recently I downloaded a video with yt-dlp. This tool puts youtube's video id into the file name in brackets, like so: video_title [video_id].webm. I wanted to mv the file and used tab completion to get its name, but the returned string did not match the video title. This was because it didn't escape the glob characters [ and ]. Hence, I had to fire up zsh to move the file.
There is currently no simple way to define a string which does not expand glob characters when working with file paths.
In #6014, it was implemented that glob expansion does not happen in " and ' strings, but only when an external command is executed.
I think this is confusing, see here:
touch a *
ls '*' # shows both files
^ls '*' # shows only one fileI believe that having behaviour differ for externals and built-ins like that is confusing and counterintuitive.
As far as I know, the only way right now to circumvent glob expansion when you're not doing external commands is by creating a character class—in which there can be no nested globs—so that *, and friends are not parsed as glob characters, like so:
touch "hello [world] *"
rm `hello [[]world[]] [*]` # this worksThis is very hard to read and I don't event know if it is the intentional way to deal with this kind of problem.
Describe the solution you'd like
- Have single-quoted (
') strings not expand globs, regardless of where they're used. - Make glob characters escapable in double-quoted (
") strings. - Remove the different treatment of glob expansion for external commands (as introduced in Conditionally disable expansion for external command #6014).
Describe alternatives you've considered
An alternative could be to do it like bash and have globs only be expanded in bare strings, but this would require cumbersome notation when mixed with spaces:
ls *.rs # expand
ls "*.rs" # don't expand
ls ("the folder/" + *.rs) # expand, mixed with space -> need concatenationThough, maybe this could in fact be an alternative if string concatenation didn't require the + operator and parentheses. (i.e. if adjacent strings were concatenated like in bash.)
Additional context and details
There's a discussion in #4631 about this. The requirements @jntrnr mentioned there would be satisfied by a non-expanding single-quoted string:
- A glob that expands automatically:
"*.rs" - A glob looking thing that doesn't expand automatically:
'*.rs' - A path with a space and a glob that expands automatically:
"my files/*.rs" - A path with a space and a glob that doesn't expand automatically
'my files/*.rs'
With Windows path syntax you could write the third bullet as"my files\\*.rs".
Also, this is a list of some issues this would fix:
- tab completion + mv results in "could not file any files matching this glob pattern" #9222, if tab completion returned single-quoted strings. (This is the same issue I had and that made me write this issue, btw.)
- Error on the commands 'cp', 'mv', 'rm', and 'open' on files that have
[]on its path #9310, because strings such as mentioned there could be enclosed in single quotes. - Wrong ... expand for some commands #5196, because
'...'and'~'wouldn't expand. - Glob expansion should not happen for external commands when quoted #4631, because strings such as mentioned there could be enclosed in single quotes. (Although this issue is outdated it seems, as globs aren't expanded on externals right now.)
Lastly, I noticed that glob behaviour is different in ls to how it is with mv, cp and rm. Here's an example:
touch a b c aa bb cc
ls ? # does not work, "directory not found"
mkdir somedir
mv ? somedir # moves the three shorter files as expectedAnother example:
touch "[]" # "escaped" file name is "[[][]]"
ls "[[][]]" # does not work, "Pattern, file or folder not found"
mv "[[][]]" nice_name # worksWhen doing the above, ls returns this error: Pattern, file or folder not found.
I believe this difference in behaviour may possibly (partially?) be caused by recent pull request #9416, as doing the same with an asterisk works fine:
touch "*"
ls "[*]" # works fine, expected outputMaybe this last part about ls belongs in its own issue, but it is somewhat related.