25

In order to remind myself when I try to use shopt in Zsh instead of setopt, I created the following alias, testing it first at a shell prompt:

$ alias shopt='echo "You\'re looking for setopt. This is Z shell, man, not Bash."'

Despite the outer single quotes matching and the inner double quotes matching, and the apostrophe being escaped, I was prompted to finish closing the quotes with:

dquote > _

What's going on?

It appeared that the escaping was being ignored, or that it needed to be double-escaped because of multiple levels of interpretation... So, just to test this theory, I tried double-escaping it (and triple-escaping it, and so on) all the way up until:

alias shopt='echo "You\\\\\\\\\\\\\\\\\\\\\\'re looking for setopt. This is Z shell, man, not Bash." '

and never saw any different behavior. This makes no sense to me. What kind of weird voodoo is preventing the shell from behaving as I expect?

The practical solution is to not use quotes for echo, since it doesn't really need any, and to use double quotes for alias, and to escape the apostrophe so it is ignored when the text is echoed. Then all of the practical problems go away.

Can you help me? I need resolution to this perplexing problem.

10
  • 4
    @muru: I would never in a million years see that other question and think it would provide an answer to my question. The first paragraph in and I have no idea what he's talking about. Wrapping a command for another command??? What does that even mean? There's too much noise from the specifics of his situation to use that as a general question for all quoting problems involving both single and double quotes.
    – iconoclast
    Commented Apr 16, 2018 at 4:16
  • 1
    @styrofoamfly I'm not asking for a mnemonic... how can it be considered the same question? Having the same subject matter (quotes) does not make it the same question.
    – iconoclast
    Commented Apr 16, 2018 at 16:40
  • 1
    There is nothing about those answers that give anyone with this same question a reason to expect to find an answer there. Whether the answer to this question is buried somewhere in the answers to those questions is irrelevant. What matters is are those the same questions? and they most certainly are not.
    – iconoclast
    Commented Apr 16, 2018 at 17:21
  • 4
    @iconoclast, if it's a single underlying question dressed up in two entirely different ways, that's exactly why we have the close-as-duplicate mechanism: To guide people to a single source for canonical answers, from multiple paths, thus providing multiple sets of keywords that lead to the same destination. This is why questions closed as duplicates can still be upvoted, aren't automatically deleted, etc -- duplicates are valuable and help the site, when they use sufficiently different terms to describe the same problem as to bring a different audience to that same destination. Commented Apr 16, 2018 at 19:53
  • 3
    @iconoclast, ...*not* closing a question as a duplicate just because it's asked in different ways leads to a place where there's no community consensus on a single set of answer, but rather competition between the two. That's unfortunate -- it spreads out the answers, and the folks who review/comment/upvote those answers, between those distinct yet equivalent-at-their-core questions. Commented Apr 16, 2018 at 19:56

5 Answers 5

46

This is zsh, man, not fish.

In zsh, like in every Bourne-like shell (and also csh), single quotes are strong quotes, there is no escaping within them (except by using the rcquotes options as hinted by @JdeBP where zsh emulates rc quotes¹). You cannot have a single quote inside a single-quoted string, you need to first close the single quoted string and enter the literal single quote using another quoting mechanism (like \ or "):

alias shopt='echo "You'\''re looking for setopt. This is Z shell, man, not Bash."'

Or:

alias shopt='echo "You'"'"'re looking for setopt. This is Z shell, man, not Bash."'

Though you could also do:

alias shopt="echo \"You're looking for setopt. This is Z shell, man, not Bash.\""

("..." are weaker quotes inside which several characters, including \ (here used to escape the embedded ") are still special).

Or:

alias shopt=$'echo "You\'re looking for setopt. This is Z shell, man, not Bash."'

($'...' is yet another kind of quotes from ksh93, where the ' can be escaped with \').

(and BTW, you can also use the standard set -o in place of setopt in zsh. bash, for historical reasons, has two sets of options, one that you set with set -o one with shopt; zsh like most other shells has only one set of options).


¹ In `rc`, the shell of Plan9, with a version for unix-likes also available, [single quotes are the only quoting mechanism](/a/296147) (backslash and double quotes are ordinary characters there), the only way to enter a literal single-quote there is with `''` inside single quotes, so with `zsh -o rcquotes`, you could do:
alias shopt='echo "You''re looking for setopt. This is Z shell, man, not Bash."'
2
  • Do you have any links to resources that describe how the ksh93 syntax works? Looks handy if that's the only thing it does.
    – jpmc26
    Commented Apr 17, 2018 at 2:59
  • 1
    @jpmc26, that's supported by most shells (and being added to the POSIX standard) so you'll probably find it described in your shell's manual. For zsh, see info zsh Quoting. Commented Apr 17, 2018 at 8:16
14
shopt='echo "You\'

This is not voodoo. This is normal POSIX shell quoting in action. There is no escaping mechanism within single-quoted strings. They always terminate at the next single quote. There is a Z shell extension that makes two successive single-quoted strings get a single quote placed between them, which you could employ. Or you could just terminate the single-quoted string, use an escaped (or indeed non-single-quote quoted) single quote, and then start a second single-quoted string.

Or you could not use contractions in your messages. ☺

Further reading

  • "Single quotes". Shell command language. Base Specifications. IEEE 1003.1:2017. The Open Group
  • "Quoting". Shell Grammar. The Z Shell manual.
3
  • Do you have any reference for that "extension"? 'foo''bar' -> foo'bar is how you get literal single quotes in rc (where single quotes are the only quoting operator). Commented Apr 15, 2018 at 22:20
  • Ah! set -o rcquotes in zsh. Commented Apr 15, 2018 at 22:27
  • 1
    Nitpick: I wouldn't use the word "terminate" because the next single quote doesn't terminate the argument, though that is what many people mistakenly believe. The crucial point to understand is that in the shell, quotes are never delimiters. Only whitespace (usually space characters) are delimiters.
    – Wildcard
    Commented Apr 16, 2018 at 19:48
14

The other answers do a good job of explaining why you're seeing this behavior. But if I may make a suggestion for how to actually solve this problem:

Don't use aliases for anything even remotely complicated.

Sure, you can tie your brain up in knots trying to figure out how to nest N layers of quotes, but it's rarely worth it for an alias. When an alias is complicated enough that its quoting becomes nontrivial, just switch to a shell function:

shopt(){
    echo "You're looking for setopt. This is Z shell, man, not Bash."
}

This removes an entire layer of quotes, allows you to add more code to the shell function later, and is generally much easier to read. It also allows much finer control over how and where arguments are inserted, instead of the alias approach of "just replace the beginning of the line and let the remaining words fall where they may." For example, with your (corrected) alias, if you type this:

shopt -s some_opt

...then you will get this output:

You're looking for setopt. This is Z shell, man, not Bash. -s some_opt

That's probably not what you wanted. The shell function will consume whatever arguments you pass to it, and silently discard them.

1
  • 3
    Yes, very good practical advice. It doesn't actually answer the question, but I'm upvoting it anyway because it's still a useful addition to the conversation.
    – iconoclast
    Commented Apr 16, 2018 at 16:45
5

For days when you don't feel like fighting the quotes, use another character like Unicode U+2019 instead of ' for apostrophes.

Press and hold Ctrl+Shift and type u2019 and this character will appear (er... depending on your locale?).

% alias shopt='echo You’re looking for setopt. This is a Z shell, woman, not Bash.'
% shopt -s dotglob
You’re looking for setopt. This is a Z shell, woman, not Bash. -s dotglob

Such usage of (U+2019) is correct because this character is officially intended to be used as an apostrophe. The Unicode Standard, Version 10.0 explicitly recommends such usage. The standard acknowledges that ' (U+0027) is commonly used as an apostrophe due to its presence in ASCII and on keyboards, and appears to permit that. But it then states that (U+2019) is preferred for apostrophes used as punctuation (and gives a contraction as an example). The only apostrophes for which (U+2019) is not preferred are those used as diacritic marks rather than punctuation marks; those are best written ʼ (U+02BC). From Apostrophes (p. 276) in section 6.2 of the standard:

Apostrophes

U+0027 APOSTROPHE is the most commonly used character for apostrophe. For historical reasons, U+0027 is a particularly overloaded character. In ASCII, it is used to represent a punctuation mark (such as right single quotation mark, left single quotation mark, apostrophe punctuation, vertical line, or prime) or a modifier letter (such as apostrophe modifier or acute accent). Punctuation marks generally break words; modifier letters generally are considered part of a word.

When text is set, U+2019 RIGHT SINGLE QUOTATION MARK is preferred as apostrophe, but only U+0027 is present on most keyboards. Software commonly offers a facility for automatically converting the U+0027 APOSTROPHE to a contextually selected curly quotation glyph. In these systems, a U+0027 in the data stream is always represented as a straight vertical line and can never represent a curly apostrophe or a right quotation mark.

Letter Apostrophe. U+02BC MODIFIER LETTER APOSTROPHE is preferred where the apostrophe is to represent a modifier letter (for example, in transliterations to indicate a glottal stop). In the latter case, it is also referred to as a letter apostrophe.

Punctuation Apostrophe. U+2019 RIGHT SINGLE QUOTATION MARK is preferred where the character is to represent a punctuation mark, as for contractions: “We’ve been here before.” In this latter case, U+2019 is also referred to as a punctuation apostrophe.

An implementation cannot assume that users’ text always adheres to the distinction between these characters. The text may come from different sources, including mapping from other character sets that do not make this distinction between the letter apostrophe and the punctuation apostrophe/right single quotation mark. In that case, all of them will generally be represented by U+2019.

The semantics of U+2019 are therefore context dependent. For example, if surrounded by letters or digits on both sides, it behaves as an in-text punctuation character and does not separate words or lines.

3
  • @StéphaneChazelas: It is my understanding that ' is also the straight quote (for when you don't know which way it goes).
    – Joshua
    Commented Apr 16, 2018 at 15:57
  • Thanks @EliahKagan. I stand corrected. I'll remove my comments. Commented Apr 19, 2018 at 16:08
  • 1
    @StéphaneChazelas Thanks. Although your previous comments were the impetus for my edit, I think the blockquoted text I added is also generally beneficial (even in the absence of those comments) and worth the length increase. Of course, as the editor who added it, I would think that, wouldn't I! Zanna: So, although I suggest keeping the full quote, I will not object if you end up deciding to shorten or remove/replace it. Commented Apr 19, 2018 at 16:15
4

A now-deleted comment tipped me off and got me half-way to the answer.

It's impossible to escape a single quote inside a single-quoted string.

This limitation is not present in double quoted strings, as I'm certainly escaping a single quotation mark inside a double-quoted string in my final solution:

alias shopt="echo You\'re looking for setopt. This is Z shell, man, not Bash."
0

Not the answer you're looking for? Browse other questions tagged .