What special meaning does an equal-sign have in zsh?
From the docs:
14.7.3 ‘=’ expansion
If a word begins with an unquoted ‘=’ and the EQUALS option is set,
the remainder of the word is taken as the name of a command. If a
command exists by that name, the word is replaced by the full pathname
of the command.
And here in more words
Zsh: Why is \n interpreted within single quotes?
zsh
itself does not interpret the \n
in 'a\nb'
, but the builtin echo
does. 'a\nb'
and "a\nb"
are equivalent and do not contain a newline but the (literal) character sequence \n
. $'a\nb'
on the other hand actually does contains a newline.
There are two things at work here:
1. Quoting:
Quoting is used to tell the shell that you want a character itself, not any special meaning it may have in the shells syntax. zsh
has four types of quoting, some of which may retain or add special meaning to a few characters or character sequences:
\
: Quoting of single characters by prepending\
. Forzsh
saying\n
only means "I want the charactern
". Asn
has no special meaning, it is the same as writing justn
. This changes with characters like*
: Without quoting*
is used as a wildcard for globbing, writing\*
prevents that (for example: compare outputs ofecho /*
andecho /\*
). If you want to pass the character\
literally, you have to quote it, for example with another\
:\\
.'...'
: Just like with\
any character withing'...'
is taken literally, this includes other methods of quoting.'foo* bar?'
is essentially equivalent tofoo\*\ bar\?
(or\f\o\o\*\ \b\a\r\?
if one wants to be pedantic). Only'
itself cannot appear inside a string quoted with'...'
as it would mark the end of the qoute. (This can be changed by settingRC_QUOTES
. If set a pair of single quote is taken as a single quote:'foo''bar'
→foo\'bar
)"..."
: Allows for parameter and command substitution. That means words after a$
are taken as parameter names (e.g."path: $PATH"
) and strings wrapped in$(...)
or`...`
are used for command substitution (e.g.echo "Date: $(date)"
). Otherwise it behaves like'...'
with the exception that it allows the quoting of`
,$
,\
and"
with\
.$'...'
: strings insid$'...'
are treated like string arguments of theprint
builtin. Only here some alphabetic characters have indeed special meaning:\n
for newline,\b
for backspace,\a
for the bell character, etc..\
and'
can be quoted with\\
and\'
respectively. The resulting string is considered fully quoted. There is no parameter or command substitution inside$'...'
.
2. The zsh-builtin echo
:
Other than the echo
binary or the bash-builtin echo
the zsh-builtin by default recognizes (some) escape sequences like \n
or \t
. While this behavior usually needs to be explicitly enabled with /bin/echo
or the bash-builtin (usually by passing the -e
flag: echo -e "foo\nbar"
), for the zsh-builtin it need to be explicitly disabled. Either by passing the -E
flag (echo -E 'foo\nbar'
) or by setting the BSD_ECHO
option (setopt bsdecho
) in which case the -e
flag can be used to re-enable the feature like with the other types of echo
.
Conclusion
That means that both 'a\nb'
and "a\nb"
(and a\\nb
for that matter) are passed as a\nb
(literally), but the zsh-builtin echo
then interprets the \n
, leading to an output with a newline. On the other hand $'a\nb'
contains a literal newline already before it is passed to echo
.
Running
for quoted_string in a\\nb 'a\nb' "a\nb" $'a\nb'; do
echo '>' $quoted_string '<'
/bin/echo -e '>' $quoted_string '<'
echo -E '>' $quoted_string '<'
/bin/echo '>' $quoted_string '<'
echo
done
should get you the following output:
> a
b <
> a
b <
> a\nb <
> a\nb <
> a
b <
> a
b <
> a\nb <
> a\nb <
> a
b <
> a
b <
> a\nb <
> a\nb <
> a
b <
> a
b <
> a
b <
> a
b <
As you can see there is no difference between the first three kinds of quoting, while the fourth always prints with a newline.
BTW: perl
(at least version 5; I do not know about Perl 6) behaves in the way you describe the expected behavior. In perl
'...'
behaves like it does in zsh
. On the other hand"..."
in perl
behaves like a combination of "..."
and $'...'
of zsh
: variables are replaced by their value and character sequences like \n
and \t
are treated specially.
Confusion about EQUALS option in Zsh
setopt
doesn't show values that currently have their default value for the current emulation mode. In Zsh emulation mode, EQUALS
is on by default, so setopt
will show only NO_EQUALS
if the option is turned off.
As to why it is on by default? The author felt it was a useful enough feature to warrant doing so. There's no "logical" reason for one choice or the other.
What are the special dollar sign shell variables?
$1
,$2
,$3
, ... are the positional parameters."$@"
is an array-like construct of all positional parameters,{$1, $2, $3 ...}
."$*"
is the IFS expansion of all positional parameters,$1 $2 $3 ...
.$#
is the number of positional parameters.$-
current options set for the shell.$$
pid of the current shell (not subshell).$_
most recent parameter (or the abs path of the command to start the current shell immediately after startup).$IFS
is the (input) field separator.$?
is the most recent foreground pipeline exit status.$!
is the PID of the most recent background command.$0
is the name of the shell or shell script.
Most of the above can be found under Special Parameters in the Bash Reference Manual. Here are all the environment variables set by the shell.
For a comprehensive index, please see the Reference Manual Variable Index.
How to define the = equals sign as a Bash alias?
Use a function:
=() { echo "foo"; }
Shell equality operators (=, ==, -eq)
=
and ==
are for string comparisons-eq
is for numeric comparisons-eq
is in the same family as -lt
, -le
, -gt
, -ge
, and -ne
==
is specific to bash (not present in sh (Bourne shell), ...). Using POSIX =
is preferred for compatibility. In bash the two are equivalent, and in sh =
is the only one that will work.
$ a=foo
$ [ "$a" = foo ]; echo "$?" # POSIX sh
0
$ [ "$a" == foo ]; echo "$?" # bash-specific
0
$ [ "$a" -eq foo ]; echo "$?" # wrong
-bash: [: foo: integer expression expected
2
(Note: make sure to quote the variable expansions. Do not leave out the double-quotes above.)
If you're writing a #!/bin/bash
script then I recommend using [[
instead. The double square-brackets [[...]]
form has more features, a more natural syntax, and fewer gotchas that will trip you up. For example, double quotes are no longer required around $a
:
$ [[ $a == foo ]]; echo "$?" # bash-specific
0
See also:
- What's the difference between [ and [[ in Bash?
Check if the parameter named by the value of x is set
There's also the -v
operator, which precludes the need for indirect parameter expansion:
if [[ -v $foo ]]; then
./my-prog
fi
This is documented in man zshmisc
, in the CONDITIONAL EXPRESSIONS section.
Bash command starting with colon with another colon before an equals signs
The :
command is the null utility:
This utility shall only expand command arguments. It is used when a command is needed, as in the then condition of an if command, but nothing is to be done by the command.
Also Bourne Shell Builtins:
Do nothing beyond expanding arguments and performing redirections. The return status is zero.
The ${foo:=bar}
syntax is a special Parameter Expansion:
${parameter:=[word]}
Assign Default Values. If parameter is unset or null, the expansion of word (or an empty string if word is omitted) shall be assigned to parameter. In all cases, the final value of parameter shall be substituted. Only variables, not positional parameters or special parameters, can be assigned in this way.
Bash Reference manual entry:
${parameter:=word}
If parameter is unset or null, the expansion of word is assigned to parameter. The value of parameter is then substituted. Positional parameters and special parameters may not be assigned to in this way.
So the command line in your question:
: ${foo:=bar}; export foo
Is two commands:
: ${foo:=bar}
export foo
The first of which expands the variable foo
and if it is empty or unset assigns it the value bar
.
The second of which then exports the foo
variable for sub-shells and other processes.
What is the purpose of the : (colon) GNU Bash builtin?
Historically, Bourne shells didn't have true
and false
as built-in commands. true
was instead simply aliased to :
, and false
to something like let 0
.
:
is slightly better than true
for portability to ancient Bourne-derived shells. As a simple example, consider having neither the !
pipeline operator nor the ||
list operator (as was the case for some ancient Bourne shells). This leaves the else
clause of the if
statement as the only means for branching based on exit status:
if command; then :; else ...; fi
Since if
requires a non-empty then
clause and comments don't count as non-empty, :
serves as a no-op.
Nowadays (that is: in a modern context) you can usually use either :
or true
. Both are specified by POSIX, and some find true
easier to read. However there is one interesting difference: :
is a so-called POSIX special built-in, whereas true
is a regular built-in.
Special built-ins are required to be built into the shell; Regular built-ins are only "typically" built in, but it isn't strictly guaranteed. There usually shouldn't be a regular program named
:
with the function oftrue
in PATH of most systems.Probably the most crucial difference is that with special built-ins, any variable set by the built-in - even in the environment during simple command evaluation - persists after the command completes, as demonstrated here using ksh93:
$ unset x; ( x=hi :; echo "$x" )
hi
$ ( x=hi true; echo "$x" )
$Note that Zsh ignores this requirement, as does GNU Bash except when operating in POSIX compatibility mode, but all other major "POSIX sh derived" shells observe this including dash, ksh93, and mksh.
Another difference is that regular built-ins must be compatible with
exec
- demonstrated here using Bash:$ ( exec : )
-bash: exec: :: not found
$ ( exec true )
$POSIX also explicitly notes that
:
may be faster thantrue
, though this is of course an implementation-specific detail.
zsh - how to reference a variable that was dynamically named after multiple other variables?
man zshexpn
provides a list of 25(!) rules that govern how expansions are processed. The problem here is that ${key1}_$key2
isn't joined into a single word until step 23, while (P)
is applied much earlier. You need a nested expansion to produce a single word upon which (P)
can be applied. To do that, you can use the :-
operator, which can omit a parameter name, expanding instead to whatever default value you provide.
% print ${:-${key1}_$key2}
a_b
Since nested substitutions are step 1 of the process, the above expression can fill in for the name expected by (P)
in step 4.
% print ${(P)${:-${key1}_$key2}}
c
Related Topics
Profiling Arbitrary Cuda Applications
Git - Crlf Issue in Windows + Linux Dual Boot
File/Directory Permissions Trailing + ( Drwxr-Xr-X+ )
Unix/Linux Ipc: Reading from a Pipe. How to Know Length of Data at Runtime
Reading Kernel Memory Using a Module
How to Cancel Command in Grunt Shell
Mmap Flag Map_Uninitialized Not Defined
Linux Device Driver Unsafe Fxsave/Fxrstor Bug - Any Precedents
Mpc/Mpd on Linux: How to Play Local Wav File
Add Timestamp to Cat Output from Shell Script
How The File Size Is Limited on a Specific File System
Hbase Does Not Run After ./Start-Hbase.Sh - Permission Denied
How to Change File Extension in Linux Shell Script
Changing File Permissions Linux
How to Change Core Pattern Only for a Particular Application