Comment échapper à des citations en shell?

66

J'ai des problèmes avec les personnages qui échappent à Bash. J'aimerais échapper aux guillemets simples et doubles lors de l'exécution d'une commande sous un autre utilisateur. Aux fins de cette question, disons que je veux faire écho à ce qui suit à l'écran:

'single quote phrase' "double quote phrase"

Comment puis-je échapper à tous les caractères spéciaux, si je dois également basculer vers un autre utilisateur:

sudo su USER -c "echo \"'single quote phrase' \"double quote phrase\"\""

Bien sûr, cela ne produit pas le bon résultat.

m33lky
la source
+1 pour "Bien sûr, cela ne donne pas les bons résultats". bashest bien sur le chemin de me rendre fou.
Rolf

Réponses:

89

Vous pouvez utiliser la syntaxe littérale de chaîne suivante:

> echo $'\'single quote phrase\' "double quote phrase"'
'single quote phrase' "double quote phrase"

De man bash

Les mots de la forme $ 'chaîne' sont traités spécialement. Le mot se développe en chaîne, avec les caractères d'échappement avec une barre oblique inversée remplacés comme spécifié par la norme ANSI C. Les séquences d'échappement de barre oblique inverse, le cas échéant, sont décodées comme suit:

          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is the octal value nnn (one to three digits)
          \xHH   the eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
          \cx    a control-x character
SiegeX
la source
5
Overkill. Dans la plupart des cas, vous n'avez pas besoin d'utiliser la syntaxe littérale de chaîne.
fpmurphy
Plus de détails ici stackoverflow.com/a/16605140/149221
mj41
12

Dans un shell POSIX, en supposant que dans votre chaîne, il n'y a pas de développement de variable, de commande ou d'historique, et il n'y a pas de saut de ligne, suivez ces recommandations de base:

  1. Pour citer une chaîne générique avec des guillemets simples, procédez comme suit:

    1. Remplacez toute séquence de caractères sans guillemets ayant la même séquence, avec des guillemets simples ajoutés et finis: 'aaa' ==> ''aaa''

    2. Echappez avec une barre oblique inverse chaque caractère guillemet simple préexistant : ' ==> \'
      en particulier,''aaa'' ==> \''aaa'\'

  2. Pour citer une chaîne générique avec des guillemets, procédez comme suit:

    1. Ajoutez des guillemets doubles de début et de fin: aaa ==> "aaa"

    2. Echappez-vous avec une barre oblique inverse pour chaque caractère guillemet double et pour chaque caractère barre oblique inversée: " ==> \", \ ==> \\

Quelques exemples:

''aaa""bbb''ccc\\ddd''  ==>  \'\''aaa""bbb'\'\''ccc\\ddd'\'\'
                        ==>  "''aaa\"\"bbb''ccc\\\\ddd''"

afin que votre exemple puisse être développé avec ce qui suit:

#!/bin/sh

echo \''aaa'\'' "bbb"'
echo "'aaa' \"bbb\""

sudo su enzotib -c 'echo \'\'\''aaa'\''\'\'\'' "bbb"'\'
sudo su enzotib -c 'echo "'\''aaa'\'' \"bbb\""'

sudo su enzotib -c "echo \\''aaa'\\'' \"bbb\"'"
sudo su enzotib -c "echo \"'aaa' \\\"bbb\\\"\""
enzotib
la source
10

Exemple simple d'écriture de citations dans un shell:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

Cela se fait en finissant un déjà ouvert ( '), en plaçant un échappé ( \'), puis en en ouvrant un autre ( ').

Alternativement:

$ echo 'abc'"'"'abc'
abc'abc
$ echo "abc"'"'"abc"
abc"abc

Cela se fait en finissant un déjà ouvert ( '), en plaçant quote dans un autre quote ( "'"), puis en en ouvrant un autre ( ').

Connexe: Comment échapper à des guillemets simples dans des chaînes à une seule citation? à stackoverflow SE

Kenorb
la source
1

La réponse acceptée fonctionne pour une citation simple (un niveau):

$ echo $'\'single quote phrase\' "double quote phrase"'
'single quote phrase' "double quote phrase"

Pour que la commande présentée fonctionne, vous devez citer deux fois.
Ce script pourrait faire tout le travail:

#!/bin/bash

quote () { 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

read -r line <<-\_line_to_quote_
'single quote phrase' "double quote phrase"
_line_to_quote_

quote "$line"; echo
quote "echo $(quote "$line")"; echo

Exécutez le script pour obtenir:

$ script
''\''single quote phrase'\'' "double quote phrase"'
'echo '\'''\''\'\'''\''single quote phrase'\''\'\'''\'' "double quote phrase"'\'''

La première ligne fonctionne pour un écho simple:

$ echo ''\''single quote phrase'\'' "double quote phrase"'
'single quote phrase' "double quote phrase"

La deuxième ligne fonctionnera pour la commande à double guillemet:

sudo su USER -c 'echo '\'''\''\'\'''\''single quote phrase'\''\'\'''\'' "double quote phrase"'\'''

la source
-3

echo 'Je suis un étudiant' ne fonctionne pas. Mais les travaux suivants:

echo $ 'Je suis un étudiant' De la page de manuel de bash:

Un seul guillemet ne peut pas apparaître entre guillemets, même s'il est précédé d'une barre oblique inverse. .... Les mots de la forme $ 'chaîne' sont traités spécialement. Le mot se développe en chaîne, avec les caractères d'échappement avec une barre oblique inversée remplacés comme spécifié par la norme ANSI C.

Sarf
la source
6
Cela n'ajoute rien aux réponses existantes.
jasonwryan