GNU/Linux >> Tutoriales Linux >  >> Linux

¿Citando en Ssh $host $foo y Ssh $host "sudo Su User -c $foo" Tipo Construcciones?

A menudo termino emitiendo comandos complejos sobre ssh; estos comandos implican canalizar a awk o perl one-lines y, como resultado, contienen comillas simples y $. No he podido encontrar una regla estricta y rápida para hacer la cotización correctamente, ni he encontrado una buena referencia para ello. Por ejemplo, considere lo siguiente:

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

(Tenga en cuenta las comillas adicionales en la declaración awk).

Pero, ¿cómo hago para que esto funcione, p. ssh $host "sudo su user -c '$CMD'" ? ¿Existe una receta general para administrar cotizaciones en tales escenarios?..

Respuesta aceptada:

Tratar con múltiples niveles de citas (en realidad, múltiples niveles de análisis/interpretación) puede resultar complicado. Es útil tener algunas cosas en mente:

  • Cada "nivel de cita" puede implicar potencialmente un idioma diferente.
  • Las reglas de cotización varían según el idioma.
  • Cuando se trata de más de uno o dos niveles anidados, por lo general es más fácil trabajar "desde abajo hacia arriba" (es decir, de más adentro a más afuera).

Niveles de cotización

Veamos sus comandos de ejemplo.

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

Su primer comando de ejemplo (arriba) usa cuatro idiomas:su shell, la expresión regular en pgrep , la expresión regular en grep (que podría ser diferente del lenguaje regex en pgrep ), y awk . Hay dos niveles de interpretación involucrados:el shell y un nivel después del shell para cada uno de los comandos involucrados. Solo hay un nivel explícito de comillas (comillas de shell en awk ).

ssh host …

A continuación, agregó un nivel de ssh en la parte superior. Este es efectivamente otro nivel de shell:ssh no interpreta el comando en sí, lo entrega a un shell en el extremo remoto (a través de (por ejemplo) sh -c … ) y ese shell interpreta la cadena.

ssh host "sudo su user -c …"

Luego preguntó acerca de agregar otro nivel de shell en el medio usando su (a través de sudo , que no interpreta sus argumentos de comando, por lo que podemos ignorarlo). En este punto, tiene tres niveles de anidamiento (awk → caparazón, caparazón → caparazón (ssh ), shell → shell (su usuario -c ), por lo que aconsejo utilizar el enfoque de "abajo hacia arriba". Asumiré que sus shells son compatibles con Bourne (por ejemplo, sh , ceniza , guion , ksh , bash , zsh , etc.). Algún otro tipo de concha (pez , rc , etc.) pueden requerir una sintaxis diferente, pero el método aún se aplica.

Abajo, Arriba

  1. Formule la cadena que desea representar en el nivel más interno.
  2. Seleccione un mecanismo de citas del repertorio de citas del siguiente idioma superior.
  3. Cotiza la cadena deseada de acuerdo con el mecanismo de cotización seleccionado.
    • A menudo hay muchas variaciones sobre cómo aplicar qué mecanismo de cotización. Hacerlo a mano suele ser cuestión de práctica y experiencia. Cuando se hace programáticamente, generalmente es mejor elegir el más fácil de hacer bien (generalmente el "más literal" (menos escapes)).
  4. Opcionalmente, utilice la cadena entrecomillada resultante con código adicional.
  5. Si aún no ha alcanzado el nivel deseado de comillas/interpretación, tome la cadena entrecomillada resultante (más cualquier código agregado) y utilícela como la cadena inicial en el paso 2.

La semántica de las citas varía

Lo que hay que tener en cuenta aquí es que cada idioma (nivel de cita) puede dar una semántica ligeramente diferente (o incluso una semántica drásticamente diferente) al mismo carácter de cita.

La mayoría de los idiomas tienen un mecanismo de comillas "literal", pero varían exactamente en qué tan literales son. La comilla simple de los shells tipo Bourne es en realidad literal (lo que significa que no puede usarla para citar un carácter de comilla simple). Otros lenguajes (Perl, Ruby) son menos literales porque interpretan algunos secuencias de barra invertida dentro de regiones con comillas simples no literalmente (específicamente, \ y ' resultado en y ' , pero otras secuencias de barra invertida son en realidad literales).

Relacionado:Php:bcompiler_write_exe_footer:escribe la posición de inicio y firma hasta el final de un archivo de tipo exe

Deberá leer la documentación de cada uno de sus idiomas para comprender sus reglas de comillas y la sintaxis general.

Tu ejemplo

El nivel más interno de su ejemplo es un awk programa.

{print $1}

Vas a incrustar esto en una línea de comando de shell:

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

Necesitamos proteger (como mínimo) el espacio y el $ en el awk programa. La opción obvia es usar comillas simples en el shell alrededor de todo el programa.

  • '{print $1}'

Sin embargo, hay otras opciones:

  • {print $1} escapar directamente del espacio y $
  • {print' $'1} comilla simple solo el espacio y $
  • "{print $1}" comillas dobles el todo y escapar del $
  • {print" $"1} comillas dobles solo el espacio y $
    Esto puede estar cambiando un poco las reglas (sin escape $ al final de una cadena entre comillas dobles es literal), pero parece funcionar en la mayoría de los shells.

Si el programa usara una coma entre las llaves de apertura y cierre, también necesitaríamos citar o escapar de la coma o de las llaves para evitar la "expansión de llaves" en algunos shells.

Seleccionamos '{print $1}' e incrustarlo en el resto del "código" de shell:

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

A continuación, quería ejecutar esto a través de su y sudo .

sudo su user -c …

su user -c … es como some-shell -c … (excepto si se ejecuta bajo algún otro UID), por lo que su simplemente agrega otro nivel de shell. sudo no interpreta sus argumentos, por lo que no agrega ningún nivel de comillas.

Necesitamos otro nivel de shell para nuestra cadena de comandos. Podemos volver a seleccionar comillas simples, pero tenemos que dar un tratamiento especial a las comillas simples existentes. La forma habitual se ve así:

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

Aquí hay cuatro cadenas que el shell interpretará y concatenará:la primera cadena entre comillas simples (pgrep … awk ), una comilla simple escapada, el awk entre comillas simples programa, otra comilla simple escapada.

Hay, por supuesto, muchas alternativas:

  • pgrep -fl java | grep -i datanode | awk '{print $1} escapar de todo lo importante
  • pgrep -fl java|grep -i datanode|awk '{print$1} lo mismo, pero sin espacios en blanco superfluos (incluso en el awk programa!)
  • "pgrep -fl java | grep -i datanode | awk '{print $1}'" comillas dobles todo, escapar del $
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'" tu variación; un poco más largo de lo habitual debido al uso de comillas dobles (dos caracteres) en lugar de escapes (un carácter)

El uso de diferentes citas en el primer nivel permite otras variaciones en este nivel:

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

Incrustando la primera variación en el sudo /*su* línea de comando da esto:

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

Puede usar la misma cadena en cualquier otro contexto de nivel de shell único (por ejemplo, ssh host … ).

A continuación, agregó un nivel de ssh en la parte superior. Este es efectivamente otro nivel de shell:ssh no interpreta el comando en sí mismo, pero lo entrega a un shell en el extremo remoto (a través de (por ejemplo) sh -c … ) y ese shell interpreta la cadena.

ssh host …

El proceso es el mismo:toma la cadena, elige un método de cotización, úsalo, insértalo.

Usando comillas simples de nuevo:

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

Ahora hay once cadenas que se interpretan y concatenan:'sudo su user -c ' , comillas simples escapadas, 'pgrep … awk ' , comilla simple con escape, barra invertida con escape, dos comillas simples con escape, la comilla simple awk program, una comilla simple con escape, una barra invertida con escape y una comilla simple con escape final.

Relacionado:¿Las cajas de GNOME arrancan desde ISO después de instalar un sistema operativo?

El formulario final se ve así:

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

Esto es un poco complicado de escribir a mano, pero la naturaleza literal de las comillas simples del shell facilita la automatización de una ligera variación:

#!/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:¿cómo rechazar un proceso en ejecución y asociarlo a un nuevo shell de pantalla?

  2. Ssh:¿restringir un usuario de Ssh/scp/sftp a un directorio?

  3. ¿Ssh y permisos de directorio de inicio?

  4. Conceptos básicos de usuario y base de datos MySQL

  5. Deshabilite el inicio de sesión raíz directo y el acceso de usuario a través de SSH al servidor

Uso de shell seguro (SSH) para inicio de sesión y copia segura (SCP) para transferencia de datos en Linux

Shell Scripting Parte 2:Aceptar entradas y realizar operaciones aritméticas de Shell

Conceptos básicos de Linux:cómo crear e instalar claves SSH en el Shell

Tunelización y proxy SSH

escriba un script de shell para ssh a una máquina remota y ejecute comandos

shell diferente para usuarios root y no root