Faire face à plusieurs niveaux de citation (vraiment, plusieurs niveaux d'analyse / d'interprétation) peut devenir compliqué. Il est utile de garder quelques points à l'esprit:
- Chaque «niveau de cotation» peut potentiellement impliquer une langue différente.
- Les règles de citation varient selon la langue.
- Lorsqu'il s'agit de plus d'un ou de deux niveaux imbriqués, il est généralement plus facile de travailler «de bas en haut» (c'est-à-dire de l'intérieur à l'extérieur).
Niveaux de cotation
Examinons vos exemples de commandes.
pgrep -fl java | grep -i datanode | awk '{print $1}'
Votre premier exemple de commande (ci-dessus) utilise quatre langages: votre shell, le regex dans pgrep , le regex dans grep (qui peut être différent du langage regex dans pgrep ) et awk . Il y a deux niveaux d'interprétation impliqués: le shell et un niveau après le shell pour chacune des commandes impliquées. Il n'y a qu'un seul niveau explicite de citation (citation de shell dans awk ).
ssh host …
Ensuite, vous avez ajouté un niveau de ssh en haut. Il s'agit en fait d'un autre niveau de shell: ssh n'interprète pas la commande elle-même, elle la remet à un shell à l'extrémité distante (via (par exemple) sh -c …
) et ce shell interprète la chaîne.
ssh host "sudo su user -c …"
Ensuite, vous avez demandé d'ajouter un autre niveau de shell au milieu en utilisant su (via sudo , qui n'interprète pas ses arguments de commande, nous pouvons donc l'ignorer). À ce stade, vous avez trois niveaux d'imbrication en cours ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c ), donc je vous conseille d'utiliser l'approche "bottom, up". Je suppose que vos coquilles sont compatibles avec Bourne (par exemple sh , ash , dash , ksh , bash , zsh , etc.) Un autre type de coquille ( poisson , rc, etc.) peut nécessiter une syntaxe différente, mais la méthode s'applique toujours.
De bas en haut
- Formulez la chaîne que vous souhaitez représenter au niveau le plus intérieur.
- Sélectionnez un mécanisme de citation dans le répertoire de citation de la prochaine langue la plus élevée.
- Citez la chaîne souhaitée en fonction du mécanisme de citation que vous avez sélectionné.
- Il existe souvent de nombreuses variantes sur la façon d'appliquer quel mécanisme de cotation. Le faire à la main est généralement une question de pratique et d'expérience. Lorsque vous le faites par programme, il est généralement préférable de choisir le plus facile à faire (généralement le «plus littéral» (le moins d'échappatoires)).
- Facultativement, utilisez la chaîne entre guillemets résultante avec du code supplémentaire.
- Si vous n'avez pas encore atteint le niveau souhaité de citation / interprétation, prenez la chaîne citée résultante (plus tout code ajouté) et utilisez-la comme chaîne de départ à l'étape 2.
Citer la sémantique varie
La chose à garder à l'esprit ici est que chaque langue (niveau de citation) peut donner une sémantique légèrement différente (ou même une sémantique radicalement différente) au même caractère de citation.
La plupart des langues ont un mécanisme de citation «littéral», mais elles varient exactement dans la mesure où elles sont littérales. La citation simple des shells de type Bourne est en fait littérale (ce qui signifie que vous ne pouvez pas l'utiliser pour citer elle-même un seul caractère de citation). D'autres langages (Perl, Ruby) sont moins littéraux en ce sens qu'ils interprètent certaines séquences de barre oblique inversée à l'intérieur de régions entre guillemets simples de manière non littérale (en particulier, \\
et \'
résultent en \
et '
, mais d'autres séquences de barre oblique inverse sont en réalité littérales).
Vous devrez lire la documentation de chacune de vos langues pour comprendre ses règles de citation et la syntaxe globale.
Votre exemple
Le niveau le plus profond de votre exemple est un programme awk .
{print $1}
Vous allez intégrer cela dans une ligne de commande shell:
pgrep -fl java | grep -i datanode | awk …
Nous devons protéger (au minimum) l'espace et $
dans le awk programme. Le choix évident est d'utiliser des guillemets simples dans le shell autour de l'ensemble du programme.
Il existe cependant d'autres choix:
{print\ \$1}
échapper directement à l'espace et $
{print' $'1}
guillemet simple seulement l'espace et $
"{print \$1}"
citer deux fois le tout et échapper à la $
{print" $"1}
double guillemet uniquement l'espace et $
cela peut plier un peu les règles (non échappé $
à la fin d'une chaîne entre guillemets est littéral), mais il semble fonctionner dans la plupart des shells.
Si le programme utilisait une virgule entre les accolades ouvertes et fermées, nous devions également citer ou échapper à la virgule ou aux accolades pour éviter «l'expansion des accolades» dans certains coques.
Nous le sélectionnons '{print $1}'
et l'intégrons dans le reste du «code» du shell:
pgrep -fl java | grep -i datanode | awk '{print $1}'
Ensuite, vous vouliez l'exécuter via su et sudo .
sudo su user -c …
su user -c …
est juste comme some-shell -c …
(sauf sous un autre UID), donc su ajoute juste un autre niveau de shell. sudo n'interprète pas ses arguments, il n'ajoute donc aucun niveau de citation.
Nous avons besoin d'un autre niveau de shell pour notre chaîne de commande. Nous pouvons à nouveau choisir des guillemets simples, mais nous devons accorder une attention particulière aux guillemets simples existants. La façon habituelle ressemble à ceci:
'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Il y a quatre chaînes ici que le shell interprétera et concaténera: la première chaîne entre guillemets simples ( pgrep … awk
), une citation simple échappée, le programme awk entre guillemets simples, une autre citation unique échappée.
Il existe bien sûr de nombreuses alternatives:
pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1}
échapper à tout ce qui est important
pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}
le même, mais sans espace blanc superflu (même dans le programme awk !)
"pgrep -fl java | grep -i datanode | awk '{print \$1}'"
citez le tout, échappez à la $
'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"
votre variation; un peu plus long que la façon habituelle en raison de l'utilisation de guillemets doubles (deux caractères) au lieu d'échappements (un caractère)
L'utilisation de citations différentes dans le premier niveau permet d'autres variations à ce niveau:
'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
'pgrep -fl java | grep -i datanode | awk {print\ \$1}'
L'incorporation de la première variation dans la ligne de commande sudo / * su * donne ceci:
sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'
Vous pouvez utiliser la même chaîne dans n'importe quel autre contexte de niveau shell unique (par exemple ssh host …
).
Ensuite, vous avez ajouté un niveau de ssh en haut. Il s'agit en fait d'un autre niveau de shell: ssh n'interprète pas la commande elle-même, mais il la remet à un shell à l'extrémité distante (via (par exemple) sh -c …
) et ce shell interprète la chaîne.
ssh host …
Le processus est le même: prenez la chaîne, choisissez une méthode de citation, utilisez-la, incorporez-la.
Utiliser à nouveau des guillemets simples:
'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
Il y a maintenant onze chaînes qui sont interprétées et concaténées 'sudo su user -c '
:, guillemet 'pgrep … awk '
simple échappé,, guillemet simple échappé, barre oblique inversée échappée, deux guillemets simples échappés, programme awk guillemet simple, guillemet simple échappé, barre oblique inversée échappée et guillemet simple échappé final .
La forme finale ressemble à ceci:
ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'
C'est un peu difficile à taper à la main, mais la nature littérale des guillemets simples du shell facilite l'automatisation d'une légère variation:
#!/bin/sh
sq() { # single quote for Bourne shell evaluation
# Change ' to '\'' and wrap in single quotes.
# If original starts/ends with a single quote, creates useless
# (but harmless) '' at beginning/end of result.
printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}
# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }
ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"
ssh host "$(sq "$s2")"