web-dev-qa-db-fra.com

Comment échapper à des guillemets simples au sein de chaînes entre guillemets simples

Disons que vous avez un Bash alias comme:

alias rxvt='urxvt'

qui fonctionne bien.

Pourtant:

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

ne fonctionnera pas, et ne sera pas non plus:

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

Alors, comment finissez-vous par faire correspondre les citations d'ouverture et de fermeture à l'intérieur d'une chaîne une fois que vous avez échappé aux citations?

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

semble disgracieux même si cela représenterait la même chaîne si vous êtes autorisé à les concaténer comme ça.

901
cons

Si vous voulez vraiment utiliser des guillemets simples dans la couche la plus externe, n'oubliez pas que vous pouvez coller les deux types de citation. Exemple:

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

Explication de la façon dont '"'"' est interprété comme juste ':

  1. ' Termine la première citation qui utilise des guillemets simples.
  2. " Commencez la deuxième citation en utilisant des guillemets doubles.
  3. ' Caractère cité.
  4. " Termine la deuxième citation en utilisant des guillemets doubles.
  5. ' Commence la troisième citation en utilisant des guillemets simples.

Si vous ne placez aucun espace entre (1) et (2) ou entre (4) et (5), le shell interprétera cette chaîne comme un long mot.

1292
liori

Je remplace toujours chaque guillemet simple incorporé par la séquence: '\'' (c'est-à-dire: guillemet guillemet guillemet) qui ferme la chaîne, ajoute un guillemet simple échappé et rouvre la chaîne.


Je crée souvent une fonction "quotify" dans mes scripts Perl pour le faire à ma place. Les étapes seraient les suivantes:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

Cela prend à peu près soin de tous les cas.

La vie devient plus amusante lorsque vous introduisez eval dans vos scripts Shell. Vous devez essentiellement tout re-citer à nouveau!

Par exemple, créez un script Perl appelé quotify contenant les instructions ci-dessus:

#!/usr/bin/Perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

puis utilisez-le pour générer une chaîne correctement citée:

$ quotify
urxvt -fg '#111111' -bg '#111111'

résultat:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

qui peut ensuite être copié/collé dans la commande alias:

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

(Si vous devez insérer la commande dans un eval, exécutez à nouveau le quotify:

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

résultat:

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

qui peut être copié/collé dans un eval:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''
234
Adrian Pronk

Depuis Bash 2.04 la syntaxe $'string' (au lieu de simplement 'string'; warning: ne confondez pas avec $('string')) est un autre mécanisme de citation qui permet ANSI C - comme des séquences d'échappement et développez la version citée en une seule fois.

Exemple simple:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

Dans ton cas:

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

Les séquences d'échappement courantes fonctionnent comme prévu:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

Ci-dessous, la documentation associée copiée-collée de man bash (version 4.4):

Les mots de la forme $ 'chaîne' sont traités spécialement. Word 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, si elles sont présentes, 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
    \?     question mark
    \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)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

Le résultat étendu est indiqué entre guillemets simples, comme si le signe dollar n'avait pas été présent.


Voir Citations et échappements: ANSI C comme des chaînes sur le wiki bash-hackers.org pour plus de détails. Notez également que "Bash Changes" fichier ( aperçu ici ) mentionne beaucoup de changements et de corrections de bugs liés au mécanisme de citation $'string'.

Selon unix.stackexchange.com Comment utiliser un caractère spécial comme un caractère normal? cela devrait fonctionner (avec quelques variantes) dans bash, zsh, mksh, ksh93, FreeBSD et busybox sh.

165
mj41

Je ne vois pas l'entrée sur son blog (lien pls?) Mais selon le manuel de référence gn :

Les caractères entre guillemets simples (‘'') préservent la valeur littérale de chaque caractère entre guillemets. Un seul guillemet ne peut pas apparaître entre guillemets, même s'il est précédé d'une barre oblique inverse.

alors bash ne comprendra pas:

alias x='y \'z '

cependant, vous pouvez le faire si vous entourez de guillemets:

alias x="echo \'y "
> x
> 'y
48
Steve B.

Je peux confirmer que l'utilisation de '\'' pour un guillemet simple dans une chaîne entre guillemets fonctionne dans Bash et peut être expliquée de la même manière que l'argument "collage" de la section précédente du fil. Supposons que nous ayons une chaîne entre guillemets: 'A '\''B'\'' C' (tous les guillemets ici sont des guillemets simples). S'il est transmis à echo, il affiche les informations suivantes: A 'B' C. Dans chaque '\'' le premier guillemet ferme la chaîne entre guillemets actuelle, le \' suivant colle un guillemet simple à la chaîne précédente (\' est un moyen de spécifier un guillemet simple sans commencer entre guillemets) et le dernier guillemet ouvre une autre chaîne entre guillemets.

28

Exemple simple de citations d'échappement dans 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 (_'_). Cette syntaxe fonctionne pour toutes les commandes. C'est une approche très similaire à la 1ère réponse.

16
kenorb

Les deux versions fonctionnent, soit avec concaténation en utilisant le caractère guillemet simple échappé (\ '), soit en concaténation en entourant le caractère guillemet simple entre guillemets doubles ("'").

L'auteur de la question n'a pas remarqué l'existence d'une citation simple supplémentaire (') à la fin de sa dernière tentative d'évasion:

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

Comme vous pouvez le voir dans la pièce Nice précédente de l'art ASCII/Unicode, la dernière citation simple échappée (\ ') est suivie d'une citation simple inutile ('). L'utilisation d'un surligneur syntaxique tel que celui présent dans Notepad ++ peut s'avérer très utile.

La même chose est vraie pour un autre exemple comme le suivant:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

Ces deux magnifiques exemples de pseudonymes montrent de manière très complexe et obscurcie la manière dont un fichier peut être aligné. En d'autres termes, à partir d'un fichier comportant beaucoup de lignes, vous n'obtenez qu'une ligne avec des virgules et des espaces entre le contenu des lignes précédentes. Afin de donner un sens au commentaire précédent, voici un exemple:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214
15
user4524350

Je ne traite pas spécifiquement de la question des citations parce que, parfois, il est juste raisonnable d’envisager une autre approche.

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

que vous pouvez ensuite appeler comme:

rxvt 123456 654321

l'idée étant que vous pouvez maintenant alias this sans vous soucier des guillemets:

alias rxvt='rxvt 123456 654321'

ou, si vous devez inclure le # dans tous les appels pour une raison quelconque:

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

que vous pouvez ensuite appeler comme:

rxvt '#123456' '#654321'

alors, bien sûr, un alias est:

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

(oups, je suppose que j'ai en quelque sorte abordé la citation :)

13
nicerobot

Je viens d'utiliser des codes Shell .. par exemple. \x27 ou \\x22 selon le cas. Pas de soucis, jamais vraiment.

10
Rob Jens

Comme il est impossible de placer des guillemets simples dans des chaînes simples, l’option la plus simple et la plus lisible consiste à utiliser une chaîne HEREDOC.

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

Dans le code ci-dessus, HEREDOC est envoyé à la commande cat et sa sortie est affectée à une variable via la notation de substitution de commande $(..).

Il est nécessaire de placer une citation autour du HEREDOC car il s’agit d’un $()

9
Nerrve

IMHO la vraie réponse est que vous ne pouvez pas échapper guillemets simples dans des chaînes guillemets simples.

C'est impossible.

Si nous supposons que nous utilisons bash.

De bash manuel ...

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.

Vous devez utiliser l'un des autres mécanismes d'échappement de chaîne "ou \

Il n’ya rien de magique dans alias qui exige qu’il utilise des guillemets simples.

Les deux suivants fonctionnent en bash.

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

Ce dernier utilise\pour échapper au caractère espace.

Il n'y a également rien de magique sur # 111111 qui nécessite des guillemets simples.

Les options suivantes permettent d'obtenir le même résultat que les deux autres options, en ce sens que l'alias rxvt fonctionne comme prévu.

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

Vous pouvez également échapper directement au # ennuyeux

alias rxvt="urxvt -fg \#111111 -bg \#111111"
6
teknopaul

La plupart de ces réponses concernent le cas spécifique sur lequel vous posez des questions. Il existe une approche générale, développée par un ami et moi-même, qui permet d'effectuer des citations arbitraires au cas où vous auriez besoin de citer des commandes bash à travers plusieurs couches d'expansion de Shell, par exemple, via ssh, su -c, bash -c, etc. Il y a un noyau primitif dont vous avez besoin, ici en bash natif:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

Cela fait exactement ce qu'il dit: il cite chaque argument individuellement (après le développement de bash, bien sûr):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

C'est la chose évidente pour une couche d'expansion:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(Notez que les guillemets doubles autour de $(quote_args ...) sont nécessaires pour transformer le résultat en un seul argument pour bash -c.) Et il peut être utilisé plus généralement pour citer correctement à travers plusieurs couches d'expansion:

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

L'exemple ci-dessus:

  1. Shell cite chaque argument individuellement dans le quote_args intérieur, puis combine le résultat obtenu en un seul argument avec les guillemets internes.
  2. Shell-quotes bash, -c, et le résultat déjà cité une fois à l'étape 1, puis combine le résultat en un seul argument avec les guillemets externes.
  3. envoie ce désordre comme argument à la bash -c extérieure.

C'est l'idée en quelques mots. Vous pouvez faire des choses assez compliquées avec cela, mais vous devez faire attention à l'ordre d'évaluation et aux sous-chaînes citées. Par exemple, ce qui suit fait ce qui est mal (pour une définition du mot "mal"):

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

Dans le premier exemple, bash étend immédiatement quote_args cd /; pwd 1>&2 en deux commandes distinctes, quote_args cd / et pwd 1>&2, de sorte que le CWD est toujours /tmp lorsque la commande pwd est exécutée . Le deuxième exemple illustre un problème similaire pour les globbing. En effet, le même problème de base se produit avec toutes les extensions de bash. Le problème ici est qu'une substitution de commande n'est pas un appel de fonction: elle évalue littéralement un script bash et utilise sa sortie dans le cadre d'un autre script bash.

Si vous essayez simplement d'échapper aux opérateurs du shell, vous échouerez, car la chaîne résultante passée à bash -c n'est qu'une séquence de chaînes entre guillemets individuels qui ne sont pas ensuite interprétées comme des opérateurs, ce qui est facile à voir si vous echo la chaîne qui aurait été passée à bash:

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

Le problème ici est que vous surestimez. Ce dont vous avez besoin, c’est que les opérateurs ne soient pas cités comme entrée dans le bash -c qui les entoure, ce qui signifie qu’ils doivent être en dehors de la substitution de la commande $(quote_args ...).

Par conséquent, ce que vous devez faire dans le sens le plus général consiste à citer Shell chaque mot de la commande non destiné à être développé séparément au moment de la substitution de commande, et à ne pas appliquer de citation supplémentaire aux opérateurs Shell:

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

Une fois que cela est fait, toute la chaîne est un jeu raisonnable pour citer davantage à des niveaux d'évaluation arbitraires:

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

etc.

Ces exemples peuvent sembler surchargés étant donné que des mots tels que success, sbin et pwd n'ont pas besoin d'être cités par Shell, mais le point clé à retenir lors de l'écriture d'un script prenant des entrées arbitraires est-ce que vous voulez citer tout ce dont vous n'êtes pas absolument sûr pas besoin de citer, parce que vous ne savez jamais quand un utilisateur publiera un Robert'; rm -rf /.

Pour mieux comprendre ce qui se passe sous les couvertures, vous pouvez jouer avec deux petites fonctions d'assistance:

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

cela énumérera chaque argument à une commande avant de l'exécuter:

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2
5
Kyle Rose

Dans l'exemple donné, utilisez simplement des guillemets doubles au lieu de guillemets simples comme mécanisme d'échappement externe:

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

Cette approche convient dans de nombreux cas où vous souhaitez simplement passer une chaîne fixe à une commande: Vérifiez simplement comment le shell interprétera la chaîne entre guillemets via un echo et les caractères d'échappement avec une barre oblique inverse, si nécessaire.

Dans l'exemple, vous verriez que les guillemets doubles suffisent à protéger la chaîne:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'
4
oberlies

Voici une élaboration de la seule vraie réponse référencée ci-dessus:

Parfois, je télécharge en utilisant rsync sur ssh et je dois échapper à un nom de fichier contenant un 'DEUX FOIS! (OMG!) Une fois pour bash et une fois pour ssh. Le même principe de délimitation de citation alternée est à l'œuvre ici.

Par exemple, disons que nous voulons obtenir: Les histoires de Louis Theroux ...

  1. Commencez par mettre Louis Theroux entre guillemets pour bash et entre guillemets pour ssh: '"Louis Theroux"'
  2. Ensuite, vous utilisez des guillemets simples pour échapper à une double citation '"'
  3. L'utilisation de guillemets doubles pour échapper à l'apostrophe "'"
  4. Répétez ensuite l'opération # 2 en utilisant des guillemets simples pour échapper à un guillemet double "" '
  5. Ensuite, placez LA Stories entre guillemets pour bash et entre guillemets pour ssh: '"LA Stories"'

Et voici! Vous vous retrouvez avec ceci:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

ce qui représente énormément de travail pour un petit - mais voilà

3
PatchyFog

ne autre façon de résoudre le problème de trop de couches de citation imbriquée:

Vous essayez de trop vous entasser dans un espace trop petit, utilisez donc une fonction bash.

Le problème est que vous essayez d'avoir trop de niveaux d'imbrication et que la technologie de base des alias n'est pas assez puissante pour prendre en charge. Utilisez une fonction bash comme celle-ci pour faire en sorte que les paramètres simples, les guillemets doubles et les paramètres transmis soient tous traités normalement, comme on pouvait s'y attendre:

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

Ensuite, vous pouvez utiliser vos variables $ 1 et $ 2 et vos guillemets simples, doubles et inverses sans vous soucier de la fonction d'alias qui détruit leur intégrité.

Ce programme imprime:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
2
Eric Leschinski
Shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

Explication de mise en oeuvre:

  • guillemets doubles afin que nous puissions facilement produire des guillemets simples et utiliser la syntaxe ${...}

  • la recherche et remplacement de bash ressemble à ceci: ${varname//search/replacement}

  • nous remplaçons ' par '\''

  • '\'' code un seul ' comme ceci:

    1. ' termine la citation simple

    2. \' code une ' (la barre oblique inverse est nécessaire car nous ne sommes pas entre guillemets)

    3. ' redémarre les guillemets simples

    4. bash concatène automatiquement les chaînes sans espace blanc entre

  • il y a \ avant chaque \ et ' parce que ce sont les règles d'échappement de ${...//.../...}.

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(Shell_escape "$string")"
echo "expanded: $(bash -c "echo $(Shell_escape "$string")")"

P.S. Encodez toujours des chaînes entre guillemets simples car elles sont beaucoup plus simples que les chaînes à guillemets doubles.

2
JasonWoof

Si vous avez GNU Parallel installé, vous pouvez utiliser sa citation interne:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

À partir de la version 20190222, vous pouvez même --shellquote plusieurs fois:

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

Il citera la chaîne dans tous les shells supportés (pas seulement bash).

1
Ole Tange

Cette fonction:

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

permet de citer ' à l'intérieur de '. Utilisez comme ceci:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

Si la ligne à citer devient plus complexe, comme les guillemets doubles mélangés à des guillemets simples, il peut s'avérer très difficile d'obtenir la chaîne à citer à l'intérieur d'une variable. Lorsque de tels cas apparaissent, écrivez la ligne exacte que vous devez citer dans un script (semblable à celui-ci).

#!/bin/bash

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

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

Est-ce que la sortie:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

Toutes les chaînes correctement citées à l'intérieur de guillemets simples.

1
user2350426

Évidemment, il serait plus facile de simplement entourer de guillemets doubles, mais quel est le défi à cet égard? Voici la réponse en utilisant seulement des guillemets simples. J'utilise une variable au lieu de alias, ce qui facilite l'impression, mais c'est la même chose que d'utiliser alias.

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

Explication

La clé est que vous pouvez fermer le devis simple et le rouvrir autant de fois que vous le souhaitez. Par exemple, foo='a''b' est identique à foo='ab'. Ainsi, vous pouvez fermer la citation simple, ajouter une citation unique littérale \', puis rouvrir la citation simple suivante.

diagramme de panne

Ce diagramme montre clairement l’utilisation de crochets pour indiquer où les guillemets simples sont ouverts et fermés. Les citations ne sont pas "imbriquées" comme le peuvent les parenthèses. Vous pouvez également faire attention à la mise en surbrillance des couleurs, qui est correctement appliquée. Les chaînes citées sont marrons, alors que le \' est en noir.

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

(Ceci est essentiellement la même réponse que celle d'Adrian, mais je pense que cela l'explique mieux. De plus, sa réponse comporte 2 guillemets simples superflus à la fin.)

1
wisbucky

Si vous générez la chaîne du shell dans Python 2 ou Python 3, voici ce qui peut être utile pour citer les arguments:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

Cela produira:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
0
George V. Reilly

Voici une autre solution. Cette fonction prendra un seul argument et le citera de manière appropriée en utilisant le caractère guillemet simple, comme l'explique la réponse votée ci-dessus:

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'"
}

Donc, vous pouvez l'utiliser de cette façon:

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

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'
0
exbuddha