Comment échapper à des guillemets simples dans des chaînes entre guillemets simples?

Disons que vous avez un alias bash comme:

 alias rxvt='urxvt' 

qui fonctionne bien

Toutefois:

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

ne marchera pas et ne le fera pas non plus:

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

Alors, comment faites-vous en sorte que les guillemets d’ouverture et de clôture se retrouvent dans une chaîne une fois que vous avez échappé aux guillemets?

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

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

Si vous voulez vraiment utiliser des guillemets simples dans la couche la plus externe, souvenez-vous que vous pouvez coller les deux types de devis. Exemple:

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

Explication de comment '"'"' est interprété comme juste ' :

  1. ' Fin de 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. " Fin de la deuxième citation, en utilisant des guillemets doubles.
  5. ' Commencez la troisième citation en utilisant des guillemets simples.

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

Je remplace toujours chaque guillemet simple incorporé par la séquence: '\'' (c’est-à-dire: guillemet de guillemets) 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 pour moi. Les étapes seraient les suivantes:

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

Cela prend en charge tous les cas.

La vie devient plus amusante lorsque vous introduisez eval dans vos scripts shell. Vous devez essentiellement tout reclasser!

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

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

utilisez-le ensuite 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 la citation:

  $ 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'\''\'\'''\'''\''' 

Depuis la syntaxe Bash 2.04, $'ssortingng' (au lieu de simplement 'ssortingng' ; warning: ne pas confondre avec $('ssortingng') ) est un autre mécanisme de citation autorisant les séquences d’échappement ANSI C-like .

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 

Vous trouverez ci-dessous la documentation relative à la copie + collée de man bash (version 4.4):

Les mots de la forme $ ‘ssortingng’ sont traités spécialement. Le mot se développe en chaîne, avec des caractères d’échappement antislash inversés comme spécifié par la norme ANSI C. Les séquences d’échappement de barre oblique inverse, si 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 entre guillemets, comme si le signe dollar n’était pas présent.


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

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

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

Enfermer des caractères entre guillemets simples (” ‘) préserve la valeur littérale de chaque caractère dans les guillemets. Une simple citation ne peut pas se produire entre des guillemets simples, même si elle est précédée d’une barre oblique inverse.

alors bash ne comprendra pas:

alias x='y \'z '

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

 alias x="echo \'y " > x > 'y 

Je peux confirmer que l’utilisation de '\'' pour une seule citation dans une chaîne entre guillemets ne fonctionne pas dans Bash, et elle peut être expliquée de la même manière que l’argument de “collage” précédent. Supposons que nous ayons une chaîne entre guillemets: 'A '\''B'\'' C' (toutes les guillemets sont des guillemets simples). S’il est transmis à l’écho, il affiche ce qui suit: A 'B' C Dans chaque '\'' la première citation ferme la chaîne de guillemets actuelle, la suivante \' colle une citation unique à la chaîne précédente ( \' est un moyen de spécifier une citation unique sans démarrer une chaîne entre guillemets), et la dernière quote ouvre une autre chaîne entre guillemets simples.

Exemple simple d’évitement des guillemets dans le 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é un ( \' ), 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.

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

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

que vous pouvez alors appeler comme:

 rxvt 123456 654321 

l’idée étant que vous pouvez maintenant créer un alias sans souci pour les citations:

 alias rxvt='rxvt 123456 654321' 

ou, si vous avez besoin d’inclure le # dans tous les appels pour une raison quelconque:

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

que vous pouvez alors 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 🙂

Je n’utilise que des codes shell, par exemple \x27 ou \\x22 selon le cas. Pas de soucis, jamais vraiment.

Comme on ne peut pas mettre des guillemets simples dans une seule chaîne entre guillemets, 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, le HEREDOC est envoyé à la commande cat et la sortie de celle-ci est affectée à une variable via la notation de substitution de commande $(..)

Il est nécessaire de placer un seul devis autour du HEREDOC car il se situe dans un $()

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

L’auteur de la question n’a pas remarqué qu’il y avait un guillemet 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 belle partie de l’art ASCII / Unicode, le dernier guillemet échappé (\ ‘) est suivi d’un simple guillemet inutile (‘). L’utilisation d’un surligneur syntaxique comme 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 belles instances d’alias montrent de manière très complexe et obscure comment un fichier peut être aligné. C’est-à-dire qu’à partir d’un fichier avec beaucoup de lignes, vous obtenez une seule ligne avec des virgules et des espaces entre les contenus des lignes précédentes. Pour 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 

La plupart de ces réponses touchent le cas spécifique dont vous parlez. Il y a une approche générale qu’un ami et moi avons développée pour permettre des citations arbitraires au cas où vous auriez besoin de citer des commandes bash via plusieurs couches d’extension de shell, par exemple, via ssh, su -c , bash -c , etc. base primitive 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 l’extension 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' 

Cela fait 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 que le résultat devienne un seul argument pour bash -c .) Et il peut être utilisé plus généralement pour citer correctement plusieurs couches de développement:

 $ 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 de la quote_args intérieure individuellement, puis combine la sortie résultante en un seul argument avec les guillemets internes.
  2. shell-quotes bash , -c et le résultat déjà cité à l’étape 1, puis combine le résultat en un seul argument avec les guillemets externes.
  3. envoie ce message comme argument à la bash -c externe.

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 qui sont citées. Par exemple, les choses suivantes font les mauvaises choses (pour une définition de “faux”):

 $ (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 développe immédiatement quote_args cd /; pwd 1>&2 quote_args cd /; pwd 1>&2 en deux commandes distinctes, quote_args cd / et pwd 1>&2 , de sorte que CWD est toujours /tmp lorsque la commande pwd est exécutée. Le deuxième exemple illustre un problème similaire pour la globalisation. En effet, le même problème de base se pose 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 un autre script bash.

Si vous essayez d’échapper simplement aux opérateurs de shell, vous échouerez car la chaîne résultante transmise à bash -c n’est qu’une séquence de chaînes entre guillemets individuelles qui ne sont pas interprétées comme des opérateurs, ce qui est facile à voir si vous faites écho à la chaîne. 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 sur-citant. Ce dont vous avez besoin, c’est que les opérateurs soient déclarés en tant qu’entrée du bash -c qui les bash -c , ce qui signifie qu’ils doivent être en dehors de la substitution de commande $(quote_args ...) .

Par conséquent, ce que vous devez faire dans le sens le plus général est de citer en citant chaque mot de la commande non destiné à être développé lors de la substitution de commande séparément, et de ne pas appliquer de devis supplémentaire aux opérateurs de 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 vous avez fait cela, la chaîne entière est un jeu équitable pour d’autres citations à 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 les mots comme success , sbin et pwd n’ont pas besoin d’être cités, mais le point clé à retenir lors de l’écriture d’un script prenant des entrées arbitraires est que vous voulez citer tout ce que vous n’êtes pas absolument sure n’a pas besoin de citer, car on ne sait jamais quand un utilisateur lancera un Robert'; rm -rf / Robert'; rm -rf / .

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

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

qui énumérera chaque argument à une commande avant de l’exécuter:

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

À mon humble avis, la vraie réponse est que vous ne pouvez pas échapper aux guillemets simples dans les chaînes entre 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 à propos des alias qui exigent 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 spatial.

Il n’y a rien de magique à propos de # 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 à la gêne # directement

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

Dans l’exemple donné, utilisez simplement des guillemets 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 à travers un echo et échapperez les caractères avec une barre oblique si nécessaire.

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

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

Voici une élaboration de la réponse unique mentionnée ci-dessus:

Parfois, je vais télécharger en utilisant rsync sur ssh et échapper à un nom de fichier avec un «deux fois! (OMG!) Une fois pour bash et une fois pour ssh. Le même principe d’alternance des délimiteurs de citation est à l’œuvre ici.

Par exemple, disons que nous voulons obtenir: LA Stories de Louis Theroux …

  1. D’abord, vous joignez Louis Theroux entre guillemets simples et doubles entre ssh: ‘”Louis Theroux”‘
  2. Ensuite, vous utilisez des guillemets simples pour échapper à un double guillemet ” ‘
  3. Les guillemets doubles pour échapper à l’apostrophe “‘”
  4. Répétez ensuite # 2, en utilisant des guillemets simples pour échapper à un guillemet double “”
  5. Puis insérez les histoires de LA dans des guillemets simples pour bash et les guillemets doubles pour ssh: ‘”LA Stories”‘

Et voici! Vous vous retrouvez avec ceci:

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

ce qui est énormément de travail pour un petit »- mais voilà

Une autre façon de résoudre le problème des trop nombreuses couches de devis nestedes:

Vous essayez de trop insérer 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 d’alias de base n’est pas assez puissante pour s’adapter. Utilisez une fonction bash comme celle-ci pour que les guillemets simples et les parameters passés soient tous traités normalement, comme prévu:

 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 arrières sans vous soucier de la fonction d’alias détruisant 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 ------------- 

Cette fonction:

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

permet de citer ' à ' intérieur ' . 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 avec des guillemets simples, il peut être très difficile d’obtenir la chaîne à citer dans une variable. Lorsque de tels cas apparaissent, écrivez la ligne exacte que vous devez citer dans un script (similaire à ceci).

 #!/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_ 

Va sortir:

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

Toutes les chaînes correctement citées dans des guillemets simples.

De toute évidence, il serait plus facile de simplement entourer de guillemets doubles, mais quel est le défi? Voici la réponse en utilisant uniquement des guillemets simples. J’utilise une variable à la place de l’ alias , c’est plus facile d’imprimer pour la preuve, mais c’est la même chose que d’utiliser un alias .

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

Explication

La clé est que vous pouvez fermer le devis unique et le rouvrir autant de fois que vous le souhaitez. Par exemple, foo='a''b' est le même que foo='ab' . Vous pouvez donc fermer le guillemet simple, insérer un guillemet simple littéral \' , puis rouvrir le guillemet suivant.

Diagramme de répartition

Ce diagramme montre clairement en utilisant des parenthèses pour montrer où les guillemets simples sont ouverts et fermés. Les citations ne sont pas “nestedes” comme peuvent l’être les parenthèses. Vous pouvez également faire attention à la mise en évidence des couleurs, qui est correctement appliquée. Les chaînes citées sont marron, alors que le \' est 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 explique mieux. Aussi sa réponse a 2 citations simples superflues à la fin.)

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

 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' 

Si vous générez la chaîne shell dans Python 2 ou Python 3, les éléments suivants peuvent vous aider à 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 va sortir:

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