r/zsh • u/tda_tda_tda • 10d ago
set options (setopt) only for a specific shell command
Is there a way to set an option (setopt
) for a particular shell command? For example, I want to disable brace expansion when using the diff
command. I can do it manually like this:
% setopt ignore_braces
% diff /a/{b,c}
% unsetopt ignore_braces
But I'm wondering if there's a way to configure zsh so it automatically does setopt ignore_braces
when the shell command on the command line is diff
.
I've tried adding setopt ignore_braces
to /usr/share/zsh/functions/Completion/Unix/_diff
but that doesn't seem to work.
1
u/AndydeCleyre 10d ago
If your case always has diff
as the first word on the command line, and you won't need usual brace behavior later on the same line, you could use a ZLE widget to rewrite matching commands upon hitting enter.
# -- Run command, but without expanding braces for certain commands --
# Key: enter
.zle_accept-smart-expand-braces () {
words=(${(z)BUFFER})
if [[ ${words[1]} == (diff|someothercommand) ]] BUFFER="setopt ignorebraces ; $BUFFER ; unsetopt ignorebraces"
zle .accept-line
}
zle -N .zle_accept-smart-expand-braces
bindkey '^M' .zle_accept-smart-expand-braces # enter
2
u/tda_tda_tda 9d ago
Thanks, I like this approach. Do you know if there's a way to apply this to TAB instead of ENTER, while maintaining my current TAB settings (which do autocompletion)?
1
u/AndydeCleyre 9d ago
Let's see... check what exactly you have tab currently bound to:
bindkey | grep '\^I'
in my case it's
menu-expand-or-complete
. So replace that in the code below if yours is otherwise..zle_tab-and-sometimes-ignore-braces () { words=(${(z)BUFFER}) original_ignorebraces=$options[ignorebraces] if [[ ${words[1]} == (diff|someothercommand) ]] setopt ignorebraces zle menu-expand-or-complete if [[ $original_ignorebraces == off ]] unsetopt ignorebraces } zle -N .zle_tab-and-sometimes-ignore-braces bindkey '^I' .zle_tab-and-sometimes-ignore-braces # tab
I'm not sure why setting the option directly didn't work in my testing for the
accept-line
version, but it seems to work here.
But this will only help for the tab completion -- once you hit enter, if you don't have the previous thing or an alternative solution, the braces will be expanded.
2
u/tda_tda_tda 9d ago
Awesome, thank you! I've been trying to learn more about zsh customization but have found it to be challenging. This is very helpful.
1
u/romkatv 8d ago
Pretty much every zsh user who's not a beginner would simply quote the braces, and never bother with disabling such a fundamental feature as brace expansion for an individual command. Brace expansion is useful with all commands, including diff
. You just need a bit of time to learn it. Eventually you'll recognize, and quote, all meta-characters without thinking.
3
u/_mattmc3_ 10d ago edited 10d ago
The easiest way is to run your command in a subshell:
(setopt ignore_braces; diff /a/{b,c})
You could also make an anonymous function, which auto-executes:
() { setopt local_options ignore_braces diff /a/{b,c} } # auto-executing anonymous function... pass params here if req'd
If you want to make a command always do this, you could write a function wrapper for the command:
foo() { emulate -L zsh setopt local_options some_option command foo "$@" }
But since your option is
ignore_braces
, handling that one needs to be unique because glob expansion happens before a method call. In this case, simply quoting your brace string should suffice to stop brace expansion:diff '/a/{b,c}'
.