r/zsh • u/darkangelstorm • 22d ago
[beginner tips] Demystifying interactive comments in scripts & command line
A addendum for readers who are learning zsh anew, or brushing up on it:
I never used "interactive" comments before but ran into it when working with a function call where I wanted a unquoted # as a parameter. I wanted to fully flesh out what was going on, as it has been on my zsh learning bucket list for a while, just never got around to it. I found out pretty quick that they are treated differently depending on whether the shell is in interactive mode or not, and some things were not quite what I expected:
This is probably beginner level material, so If you are versed in scripting under zsh, you probably already know this stuff. Personally I am not all that intelligent compared to others, but if you are like me, some explanation on what would probably had been super easy for me 10 years ago, now explained. Things like this remind me that maybe I shouldn't have tried to learn EVERY language 😹
#!/bin/zsh
#
if [[ ...something ]]]; then
function_name some paramaters # this is an interactive comment, or is it?
fi
Actually, that is not an interactive comment. 😦
But this might be....
that1user@why.net:~# some_program some_args # THIS is an interactive comment!!
but only after you do this...
that1user@why.net:~# setopt in_tera_c__tiveCOMmentS
and just for fun, a bit of double negativity...
that1user@why.net:~# unsetopt no___in_tera_c__tiveCOMmentS
(note: zsh does not care about case nor the existence of underscores and reverses the meaning when "no" comes first) :3
Use comments on the command line, in scripts, and use of setopt interactivecomments can be somewhat misleading. For one, it has NO EFFECT in executed file scripts.
When I talk about "executed scripts" some examples are:
/bin/zsh /path/to/scriptname- script executed directly with zsh./scriptname- script executed from same directory with +x permissionsscriptname("scriptname" is in yourPATHand marked executable).
Not to be confused with "sourcing", some examples which are:
source scriptname- using the 'source' built-in keyword. scriptname- using the '.' builtin syntax (space required after, must be first)
The interactivecomments option is only for interactive mode. Comments within scripts will not ignore comments on the same line because executed scripts are ALWAYS running with the interactive option turned off (and it cannot be turned on by force).
This is misleading for two reasons:
Firstly, because interactivecomments can be turned on and off even within scripts where interactive cannot be turned on!! The word 'interactive' is easily misunderstood here to mean the fact that the comments are interacting with the command on the same line. Instead, it is referring to the state of the interactive flag which is always ON when sourcing or in the prompt, and always OFF when running scripts.
Secondly, and this is the crux, it is misleading because what is actually happening is that any characters following the $HISTCHARS[3] single-character are treated as comments. It doesn't care if it is a hash (#) or not, the hashtag just happens to be the default value for $HISTCHARS[3] This means to REALLY disable it, you have to set that to something you won't be using (like a null, $'\0' for example).
A quick warning about the side effects of using HISTCHARS
Though it may be obvious, some might not realize what disabling a comment character is going to do, should you have already, actual comments in the script (chances are, you do). So I decided to write this extra bit to ensure everyone knows of one of the biggest caveats to using the HISTCHARS method.
Changing HISTCHARS[3] in scripts means that any comments outside the shebang --a line which is discarded before actual script interpretation-- are going to treated as REGULAR commands, not comments! I want to stress that this is probably not the best idea unless you just don't use comments. That said, the next person managing your script might. You have the following options available to you:
- Don't change HISTCHARS[3] (safest route) however inline comments will still be read in scripts
- Use another comment character, which you'll want to be 8-bit to keep compatibility with other character sets. UTF-8 is a multi-byte encoded set (meaning usually they are 8-bit, but when higher codes are needed, they can span as far as 6 bytes in width or more). Probably not very portable, especially on legacy machines.
- Do it anyway and leave a comment about what you did... (can you see the problem with this choice?)
On the other hand, if you are a disgruntled employee, maybe this is for you :3 After all, that new guy might not make the connection that HISTCHARS has anything to do with comments. You can sit at home each night toasting champagne as you evilly laugh and wait for your ex-boss to leave a voicemail pleading for you to take your job back and fix the issues. Don't hold your breath though :3
The rest of the documentation will assume you actually read the above warning....
More on INTERACTIVE_COMMENTS and $HISTCHARS[3] (or $histchars[3])
Again, the interactivecomments could be written INTERACTIVE_COMMENTS, or interACTIVEcOMEnTs, zsh doesnt care about _ or case in options and negates the option if any form of 'no' preceeds it.
It enables/disables evaluation of $HISTCHARS[3] ONLY when said shell also has the interactive shell option set. Obviously, you can't set that option in an executing script, which is why it ignores you when you set it there:
#!/bin/zsh
someprogram arg # these would be ignored
would launch "someprogram arg"
Versus:
#!/bin/zsh
histchars[3]=$'\0'
# yikes, this gets executed, too!
someprogram arg # these are not ignored
would launch "someprogram arg # these are not ignored"
You will want to be sure to slap in a 'noglob' if you have glob characters in there.
#!/bin/zsh
histchars[3]=$'\0'
# yikes this line is executed!
<NUL> this would be a real comment at this point
noglob someprogram arg # these are not ignored???<- because we dont want globbing here
This way you don't cause a no glob match error should you use special glob characters like the question mark, etc. Be sure to note that ALL #comments above become invalid once you modify histchars (or HISTCHARS).
ZSH does not straightforwardly address this. Rather they kinda beat around the bush, eventually addressing stuff but only if you look each thing up in turn, and piece it together like you are diagnosing an A/C unit.
In closing, another equally frustrating we will cover at some point that is much like HISTCHARS, is IFS (stands for Input Field Separators) which is used to control (final) word splitting on the command line, in arrays, and a bunch of other places. Like HISTCHARS, it can be a power but also a disaster-causing variable if misused.
"this thing reads like stereo instructions" is so true when it comes to the zsh documentation.
This is an AI-free post. No AI was used to make it or research it. I value human created content even if it is considered silly by many to do so. We live our lives the way that makes us happy, nothing wrong with that. Hope this was somehow useful for you. No responses are expected or required, I am happy to just make this info available. Have a nice day!