Comment échapper à un guillemet double à l'intérieur de guillemets doubles?

286

Comment puis-je échapper les guillemets doubles à l'intérieur d'une chaîne double dans Bash?

Par exemple, dans mon script shell

#!/bin/bash

dbload="load data local infile \"'gfpoint.csv'\" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \"'\n'\" IGNORE 1 LINES"

Je ne peux pas obtenir la ENCLOSED BY '\"'double citation pour m'échapper correctement. Je ne peux pas utiliser de guillemets simples pour ma variable, car je veux utiliser une variable $dbtable.

Sean Nguyen
la source
1
Voir aussi mywiki.wooledge.org/BashFAQ/050
Charles Duffy
duplication possible de guillemets simples
kenorb
2
@kenorb ne ressemble pas à un double de cette question ...
Mark
@Daenyth Ce n'est pas le type de commande auquel vous vous attendez à ce que les utilisateurs finaux aient accès. Les scripts de chargement en masse sont généralement exécutés sur le serveur par des utilisateurs de confiance (tels que les administrateurs système ou les développeurs). Oui, si les utilisateurs finaux contrôlent la valeur de $dbtable, il y a un risque. Ce serait très rare, cependant, car les utilisateurs finaux ne font généralement pas de SSH sur une machine pour charger leurs données.
jpmc26

Réponses:

285

Utilisez une barre oblique inverse:

echo "\""     # Prints one " character.
Peter
la source
9
Ca ne fonctionne pas. x=ls; if [ -f "$(which "\""$x"\"")" ]; then echo exists; else echo broken; fi;donne cassé tandis que ... [ -f "$(which $x)" ]; ...ou ... [ -f $(which "$x") ]; ...fonctionne très bien. Des problèmes surgiraient lorsque l'un $xou le résultat de $(which "$x")donnerait quelque chose avec un espace ou un autre caractère spécial. Une solution de contournement utilise une variable pour contenir le résultat which, mais bash est-il vraiment incapable d'échapper à un devis ou est-ce que je fais quelque chose de mal?
Luc
J'essaie d'utiliser ce qui suit grep -oh "\"\""$counter"\""\w*" dans le cadre d'une syntaxe bash où in $counterest une variable. il n'aime pas les pensées
Jay D
82

Un exemple simple d'échapper des guillemets dans le shell:

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

Pour ce faire, vous devez terminer celui déjà ouvert ( '), placer celui échappé ( \'), puis en ouvrir un autre ( ').

Alternativement:

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

Pour ce faire, vous devez terminer un ( ') déjà ouvert , placer un devis dans un autre quote ( "'"), puis en ouvrir un autre ( ').

Plus d'exemples: échappement de guillemets simples dans des chaînes entre guillemets simples

Kenorb
la source
1
J'ai essayé sh -c "echo '{" key ":" value "}'" et même sh -c "echo '{' '"' 'key' '"' ':' '"' 'value' '"' '}' "dans un effort pour mettre les mots clé et valeur entre guillemets, mais dans les deux cas, j'ai {key: value}
Igor Yagolnitser
1
Cela semble inutilement compliqué pour les guillemets doubles: il echo "abc\"abc"suffit de produire abc"abccomme dans la réponse de Peter.
divenex
2
Dans cet exemple simple en effet, mais dans les cas complexes de guillemets imbriqués, il peut être nécessaire de le faire et l'exemple de @ kenorb m'a aidé à comprendre comment traiter ces cas.
prosoitos
63

Je ne sais pas pourquoi cet ancien problème est apparu aujourd'hui dans les listes marquées Bash, mais juste au cas où de futurs chercheurs, gardez à l'esprit que vous pouvez éviter de vous échapper en utilisant les codes ASCII des caractères que vous devez faire écho.

Exemple:

 echo -e "This is \x22\x27\x22\x27\x22text\x22\x27\x22\x27\x22"
 This is "'"'"text"'"'"

\x22est le code ASCII (en hexadécimal) pour les guillemets doubles et \x27pour les guillemets simples. De même, vous pouvez faire écho à n'importe quel caractère.

Je suppose que si nous essayons de faire écho à la chaîne ci-dessus avec des barres obliques inverses, nous aurons besoin d'un écho désordonné à deux lignes ... :)

Pour l'affectation des variables, c'est l'équivalent:

 $ a=$'This is \x22text\x22'
 $ echo "$a"
 This is "text"

Si la variable est déjà définie par un autre programme, vous pouvez toujours appliquer des guillemets doubles / simples avec sed ou des outils similaires.

Exemple:

 $ b="Just another text here"
 $ echo "$b"
 Just another text here
 $ sed 's/text/"'\0'"/' <<<"$b" #\0 is a special sed operator
 Just another "0" here #this is not what i wanted to be
 $ sed 's/text/\x22\x27\0\x27\x22/' <<<"$b"
 Just another "'text'" here #now we are talking. You would normally need a dozen of backslashes to achieve the same result in the normal way.
George Vasiliou
la source
1
+1 parce qu'il a résolu un problème d'ajout d'une variable PS1 à ~ / .profile echo 'export PS1='\[\033[00;31m\]${?##0}$([ $? -ne 0 ] && echo \x22 \x22)\[\033[00;32m\]\u\[\033[00m\]@\[\033[00;36m\]\h\[\033[00m\][\[\033[01;33m\]\d \t\[\033[00m\]] \[\033[01;34m\]\w\n\[\033[00m\]$( [ ${EUID} -ne 0 ] && echo \x22$\x22 || echo \x22#\x22 ) '' >> ~/.profile
Yassine ElBadaoui
1
C'est LA réponse! Je t'aime monsieur.
Miguel Rojas Cortés
28

Bash vous permet de placer des chaînes de façon adjacente, et elles finiront par être collées ensemble.

Donc ça:

$ echo "Hello"', world!'

produit

Hello, world!

L'astuce consiste à alterner entre les chaînes simples et doubles entre guillemets selon les besoins. Malheureusement, cela devient rapidement très compliqué. Par exemple:

$ echo "I like to use" '"double quotes"' "sometimes"

produit

I like to use "double quotes" sometimes

Dans votre exemple, je le ferais quelque chose comme ceci:

$ dbtable=example
$ dbload='load data local infile "'"'gfpoint.csv'"'" into '"table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"'"'"' LINES "'TERMINATED BY "'"'\n'"'" IGNORE 1 LINES'
$ echo $dbload

qui produit la sortie suivante:

load data local infile "'gfpoint.csv'" into table example FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "'\n'" IGNORE 1 LINES

Il est difficile de voir ce qui se passe ici, mais je peux l'annoter en utilisant des guillemets Unicode. Ce qui suit ne fonctionnera pas dans bash - c'est juste pour l'illustration:

dbload=' load data local infile "' " 'gfpoint.csv'" ' " into' " table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '" ' "' " ' LINES" ' TERMINATED BY "' " '\n'" ' " IGNORE 1 LINES'

Les guillemets comme «'» »ci-dessus seront interprétés par bash. Les guillemets comme " 'se retrouveront dans la variable résultante.

Si je donne le même traitement à l'exemple précédent, cela ressemble à ceci:

$ echo" I like to use" ' "double quotes"' " sometimes"

Scarabée
la source
4
Oof ... c'est quelque chose de laid.
Urne de poulpe magique
17

Découvrez printf ...

#!/bin/bash
mystr="say \"hi\""

Sans utiliser printf

echo -e $mystr

Sortie: dites "salut"

Utilisation de printf

echo -e $(printf '%q' $mystr)

Sortie: dites \ "salut \"

Danny Hong
la source
2
Notez que cela printféchappe également à d'autres personnages, tels que ', (et)
David Pärsson
printf %qgénère des chaînes prêtes pour eval, non formatées pour echo -e.
Charles Duffy
2
Il n'y a aucune raison d'envelopper le printfavec une utilisation inutile deecho . Vos deux exemples ont rompu les citations. Le correctif correct consiste à mettre entre guillemets la variable.
tripleee
15

Stockez le caractère guillemet double comme variable:

dqt = '"'
echo "Double guillemets $ {dqt} X $ {dqt} dans une chaîne entre guillemets"

Production:

Double guillemets "X" à l'intérieur d'une chaîne entre guillemets doubles
12oclocker
la source
39
Bash est vraiment la pire langue
Andy Ray
@ 12oclocker, votre réponse est infaillible: D! spécialement lors de l'utilisation avec la commande "sed" cela m'a sauvé la journée!
Artanis Zeratul
11

Utilisez $ "string".

Dans cet exemple, ce serait,

dbload = $ "charger les données du fichier local \" 'gfpoint.csv' \ "dans la table $ dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY \ "'\ n' \" IGNORE 1 LINES "

Remarque (à partir de la page de manuel ):

Une chaîne entre guillemets précédée d'un signe dollar ($ "chaîne") entraînera la traduction de la chaîne en fonction des paramètres régionaux actuels. Si les paramètres régionaux actuels sont C ou POSIX, le signe dollar est ignoré. Si la chaîne est traduite et remplacée, le remplacement est entre guillemets.

Trisha Chatterjee
la source
3
Bien, je ne connaissais pas celui-là.
David Kierans
-5

Ajouter "\"avant les guillemets doubles pour l'échapper, au lieu de\

#! /bin/csh -f

set dbtable = balabala

set dbload = "load data local infile "\""'gfpoint.csv'"\"" into table $dbtable FIELDS TERMINATED BY ',' ENCLOSED BY '"\""' LINES TERMINATED BY "\""'\n'"\"" IGNORE 1 LINES"

echo $dbload
# load data local infile "'gfpoint.csv'" into table balabala FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY "''" IGNORE 1 LINES
Shilv
la source
6
Downvote: Pourquoi publiez-vous une cshréponse à une bashquestion? Les deux sont complètement distincts et incompatibles.
tripleee