How to escape single quotes within single quoted s

2018-12-31 02:39发布

Let's say, you have a bash alias like:

alias rxvt='urxvt'

which works fine.

However:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

won't work, and neither will:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

So how do you end up matching up opening and closing quotes inside a string once you have escaped quotes?

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

seems ungainly although it would represent the same string if you're allowed to concatenate them like that.

19条回答
余生请多指教
2楼-- · 2018-12-31 02:48

If you really want to use single quotes in the outermost layer, remember that you can glue both kinds of quotation. Example:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

Explanation of how '"'"' is interpreted as just ':

  1. ' End first quotation which uses single quotes.
  2. " Start second quotation, using double-quotes.
  3. ' Quoted character.
  4. " End second quotation, using double-quotes.
  5. ' Start third quotation, using single quotes.

If you do not place any whitespaces between (1) and (2), or between (4) and (5), the shell will interpret that string as a one long word.

查看更多
有味是清欢
3楼-- · 2018-12-31 02:48

IMHO the real answer is that you can't escape single-quotes within single-quoted strings.

Its impossible.

If we presume we are using bash.

From bash manual...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

You need to use one of the other string escape mechanisms " or \

There is nothing magic about alias that demands it use single quotes.

Both the following work in bash.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

The latter is using \ to escape the space character.

There is also nothing magic about #111111 that requires single quotes.

The following options achieves the same result the other two options, in that the rxvt alias works as expected.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

You can also escape the troublesome # directly

alias rxvt="urxvt -fg \#111111 -bg \#111111"
查看更多
长期被迫恋爱
4楼-- · 2018-12-31 02:48

Obviously, it would be easier simply to surround with double quotes, but where's the challenge in that? Here is the answer using only single quotes. I'm using a variable instead of alias so that's it's easier to print for proof, but it's the same as using alias.

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

Explanation

The key is that you can close the single quote and re-open it as many times as you want. For example foo='a''b' is the same as foo='ab'. So you can close the single quote, throw in a literal single quote \', then reopen the next single quote.

Breakdown diagram

This diagram makes it clear by using brackets to show where the single quotes are opened and closed. Quotes are not "nested" like parentheses can be. You can also pay attention to the color highlighting, which is correctly applied. The quoted strings are maroon, whereas the \' is black.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(This is essentially the same answer as Adrian's, but I feel this explains it better. Also his answer has 2 superfluous single quotes at the end.)

查看更多
深知你不懂我心
5楼-- · 2018-12-31 02:49

I'm not specifically addressing the quoting issue because, well, sometimes, it's just reasonable to consider an alternative approach.

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

which you can then call as:

rxvt 123456 654321

the idea being that you can now alias this without concern for quotes:

alias rxvt='rxvt 123456 654321'

or, if you need to include the # in all calls for some reason:

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

which you can then call as:

rxvt '#123456' '#654321'

then, of course, an alias is:

alias rxvt="rxvt '#123456' '#654321'"

(oops, i guess i kind of did address the quoting :)

查看更多
一个人的天荒地老
6楼-- · 2018-12-31 02:49

Another way to fix the problem of too many layers of nested quotation:

You are trying to cram too much into too tiny a space, so use a bash function.

The problem is you are trying to have too many levels of nesting, and the basic alias technology is not powerful enough to accommodate. Use a bash function like this to make it so the single, double quotes back ticks and passed in parameters are all handled normally as we would expect:

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

Then you can use your $1 and $2 variables and single, double quotes and back ticks without worrying about the alias function wrecking their integrity.

This program prints:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
查看更多
倾城一夜雪
7楼-- · 2018-12-31 02:49

Here is another solution. This function will take a single argument and appropriately quote it using the single-quote character, just as the voted answer above explains:

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

So, you can use it this way:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
查看更多
登录 后发表回答