GNU/Linux >> Tutoriels Linux >  >> Linux

Citation dans les constructions de type Ssh $host $foo et Ssh $host "sudo Su User -c $foo" ?

Je finis souvent par émettre des commandes complexes sur ssh; ces commandes impliquent une canalisation vers awk ou perl sur une seule ligne et, par conséquent, contiennent des guillemets simples et des $. Je n'ai ni été en mesure de trouver une règle stricte et rapide pour faire la citation correctement, ni trouvé une bonne référence pour cela. Par exemple, considérez ce qui suit :

# what I'd run locally:
CMD='pgrep -fl java | grep -i datanode | awk '{print $1}'
# this works with ssh $host "$CMD":
CMD='pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'"

(Notez les guillemets supplémentaires dans l'instruction awk.)

Mais comment puis-je faire fonctionner cela, par ex. ssh $host "sudo su user -c '$CMD'" ? Existe-t-il une recette générale pour gérer les devis dans de tels scénarios ?..

Réponse acceptée :

Traiter avec plusieurs niveaux de citation (en fait, plusieurs niveaux d'analyse/interprétation) peut devenir compliqué. Il est utile de garder à l'esprit quelques éléments :

  • Chaque "niveau de citation" peut potentiellement impliquer une langue différente.
  • Les règles de citation varient selon la langue.
  • Lorsque vous traitez avec plus d'un ou deux niveaux imbriqués, il est généralement plus simple de travailler "du bas vers le haut" (c'est-à-dire de l'intérieur vers l'extérieur).

Niveaux de citation

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, la regex dans pgrep , la 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 (shell citant 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, il la transmet à un shell du côté distant (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 (awk → shell, shell → shell (ssh ), shell → shell (su user -c ), je conseille donc d'utiliser l'approche "bas, haut". Je supposerai que vos coques sont compatibles Bourne (par exemple, sh , cendre , tiret , ksh , bash , zsh , etc.). Un autre type de coquillage (poisson , rc , etc.) peut nécessiter une syntaxe différente, mais la méthode s'applique toujours.

Bas, Haut

  1. Formulez la chaîne que vous souhaitez représenter au niveau le plus interne.
  2. Sélectionnez un mécanisme de guillemets dans le répertoire de guillemets de la langue immédiatement supérieure.
  3. Citez la chaîne souhaitée en fonction du mécanisme de citation que vous avez sélectionné.
    • Il existe souvent de nombreuses variantes pour 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 programmation, il est généralement préférable de choisir le plus facile à obtenir (généralement le "plus littéral" (le moins d'échappements)).
  4. Facultatif, utilisez la chaîne entre guillemets résultante avec du code supplémentaire.
  5. Si vous n'avez pas encore atteint le niveau de citation/interprétation souhaité, prenez la chaîne entre guillemets résultante (plus tout code ajouté) et utilisez-la comme chaîne de départ à l'étape 2.

La sémantique des citations 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 langages ont un mécanisme de citation "littéral", mais ils varient exactement dans leur degré de littéralité. Le guillemet simple des shells de type Bourne est en fait littéral (ce qui signifie que vous ne pouvez pas l'utiliser pour citer un guillemet simple lui-même). D'autres langages (Perl, Ruby) sont moins littéraux dans la mesure où ils interprètent certains séquences de barres obliques inverses à l'intérieur de régions entre guillemets simples non littéralement (en particulier, \ et ' résultat en et ' , mais d'autres séquences de barres obliques inverses sont en fait littérales).

Connexe :Php :bcompiler_write_exe_footer — Écrit le point de départ et sig à la fin d'un fichier de type exe

Vous devrez lire la documentation de chacun de vos langages pour comprendre ses règles de citation et la syntaxe globale.

Votre exemple

Le niveau le plus profond de votre exemple est un awk programme.

{print $1}

Vous allez intégrer ceci dans une ligne de commande shell :

pgrep -fl java | grep -i datanode | awk …

Nous devons protéger (au minimum) l'espace et le $ dans le awk programme. Le choix évident est d'utiliser des guillemets simples dans le shell autour de l'ensemble du programme.

  • '{print $1}'

Il existe cependant d'autres choix :

  • {print $1} échapper directement l'espace et $
  • {print' $'1} apostrophe uniquement l'espace et $
  • "{print $1}" guillemet le tout et échappe le $
  • {print" $"1} guillemet uniquement l'espace et $
    Cela peut contourner un peu les règles ($ non échappé à la fin d'une chaîne entre guillemets doubles est littéral), mais cela semble fonctionner dans la plupart des shells.

Si le programme utilisait une virgule entre les accolades ouvrantes et fermantes, nous aurions également besoin de citer ou d'échapper la virgule ou les accolades pour éviter "l'expansion des accolades" dans certains shells.

Nous sélectionnons '{print $1}' et intégrez-le dans le reste du "code" du shell :

pgrep -fl java | grep -i datanode | awk '{print $1}'

Ensuite, vous vouliez exécuter ceci via su et sudo .

sudo su user -c …

su user -c … est comme some-shell -c … (sauf en cours d'exécution sous un autre UID), donc su ajoute simplement 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 sélectionner les guillemets simples, mais nous devons accorder une gestion spéciale aux guillemets simples existants. La manière 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 ), un guillemet simple échappé, le guillemet simple awk programme, un autre guillemet simple échappé.

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 superflu (même dans le awk programme !)
  • "pgrep -fl java | grep -i datanode | awk '{print $1}'" guillemet le tout, échappez le $
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'" votre variante ; un peu plus long que la manière 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 au 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}'

Intégrer la première variation dans le sudo /*su* la ligne de commande 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 la transmet à un shell du côté distant (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, intégrez-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 simple échappé, 'pgrep … awk ' , guillemet simple échappé, barre oblique inverse échappée, deux guillemets simples échappés, le guillemet simple awk programme, un guillemet simple échappé, une barre oblique inverse échappée et un guillemet simple échappé final.

En relation:GNOME Boxes Boot From ISO Après L'Installation D'un Système D'exploitation?

Le formulaire final ressemble à ceci :

ssh host 'sudo su user -c '''pgrep -fl java | grep -i datanode | awk ''\'''{print $1}''\'

C'est un peu compliqué à 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 '%sn' "$*" | 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")"

Linux
  1. Ssh – Restreindre un utilisateur Ssh/scp/sftp à un répertoire ?

  2. Bases de l'utilisateur et de la base de données MySQL

  3. CentOS / RHEL :Comment désactiver / activer la connexion ssh directe des utilisateurs root et non root

  4. Quelle est la différence entre /bin/false et /sbin/nologin en tant que shell de l'utilisateur nologin

  5. écrire un script shell en ssh sur une machine distante et exécuter des commandes

Utilisation de Secure Shell (SSH) pour la connexion et la copie sécurisée (SCP) pour le transfert de données sous Linux

Shell Scripting Partie 2 :Acceptation des entrées et exécution de l'arithmétique Shell

Principes de base de Linux :Comment créer et installer des clés SSH sur le shell

Comment restreindre l'accès SSH pour l'utilisateur avec LShell (shell limité)

Tunnellisation et proxy SSH

accès au tunneling ssh uniquement