GNU/Linux >> Linux Esercitazione >  >> Linux

Citazione in Ssh $host $pippo e Ssh $host "sudo Su User -c $pippo" Costrutti di tipo?

Spesso finisco per emettere comandi complessi su ssh; questi comandi implicano il piping su awk o perl one-lines e, di conseguenza, contengono virgolette singole e $. Non sono stato in grado di escogitare una regola rigida e veloce per citare correttamente, né ho trovato un buon riferimento per questo. Ad esempio, considera quanto segue:

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

(Nota le virgolette extra nella dichiarazione awk.)

Ma come faccio a farlo funzionare, ad es. ssh $host "sudo su user -c '$CMD'" ? Esiste una ricetta generale per la gestione dei preventivi in ​​tali scenari?..

Risposta accettata:

Gestire più livelli di citazione (in realtà, più livelli di analisi/interpretazione) può diventare complicato. Aiuta a tenere a mente alcune cose:

  • Ogni "livello di citazione" può potenzialmente coinvolgere una lingua diversa.
  • Le regole per le citazioni variano in base alla lingua.
  • Quando si ha a che fare con più di uno o due livelli nidificati, di solito è più facile lavorare "dal basso verso l'alto" (cioè dal più interno al più esterno).

Livelli di quotazione

Diamo un'occhiata ai tuoi comandi di esempio.

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

Il tuo primo comando di esempio (sopra) usa quattro linguaggi:la tua shell, l'espressione regolare in pgrep , l'espressione regolare in grep (che potrebbe essere diverso dalla lingua regolare in pgrep ), e awk . Ci sono due livelli di interpretazione coinvolti:la shell e un livello dopo la shell per ciascuno dei comandi coinvolti. C'è solo un livello esplicito di citazione (citazione della shell in awk ).

ssh host …

Successivamente hai aggiunto un livello di ssh in cima. Questo è effettivamente un altro livello di shell:ssh non interpreta il comando stesso, lo passa a una shell sull'estremità remota (tramite (ad es.) sh -c … ) e quella shell interpreta la stringa.

ssh host "sudo su user -c …"

Quindi hai chiesto di aggiungere un altro livello di shell nel mezzo usando su (tramite sudo , che non interpreta i suoi argomenti di comando, quindi possiamo ignorarlo). A questo punto, hai tre livelli di annidamento in corso (awk → shell, shell → shell (ssh ), shell → shell (su user -c ), quindi consiglio di utilizzare l'approccio "dal basso verso l'alto". Presumo che le tue shell siano compatibili con Bourne (ad es. sh , cenere , trattino , ksh , bash , zsh , eccetera.). Qualche altro tipo di conchiglia (pesce , rc , ecc.) potrebbe richiedere una sintassi diversa, ma il metodo si applica comunque.

Basso, Su

  1. Formula la stringa che vuoi rappresentare al livello più interno.
  2. Seleziona un meccanismo di virgolette dal repertorio di virgolette della lingua immediatamente successiva.
  3. Citare la stringa desiderata in base al meccanismo di quotazione selezionato.
    • Ci sono spesso molte variazioni su come applicare quale meccanismo di quotazione. Farlo a mano è solitamente una questione di pratica ed esperienza. Quando lo fai in modo programmatico, di solito è meglio scegliere il modo più facile per ottenere il risultato giusto (di solito il "più letterale" (il minor numero di escape)).
  4. Facoltativamente, utilizza la stringa tra virgolette risultante con codice aggiuntivo.
  5. Se non hai ancora raggiunto il livello di citazione/interpretazione desiderato, prendi la stringa tra virgolette risultante (più qualsiasi codice aggiunto) e usala come stringa iniziale nel passaggio 2.

Citare la semantica varia

La cosa da tenere a mente qui è che ogni lingua (livello di virgolette) può dare una semantica leggermente diversa (o anche una semantica drasticamente diversa) allo stesso carattere di virgolette.

La maggior parte delle lingue ha un meccanismo di virgolette "letterali", ma variano esattamente in quanto letterali sono. La virgoletta singola delle shell tipo Bourne è in realtà letterale (il che significa che non puoi usarla per citare un singolo carattere di virgolette). Altre lingue (Perl, Ruby) sono meno letterali in quanto ne interpretano alcuni sequenze di barre rovesciate all'interno di regioni tra virgolette singole non letteralmente (in particolare, \ e ' risultato in e ' , ma altre sequenze di barre rovesciate sono in realtà letterali).

Correlati:Php:bcompiler_write_exe_footer — Scrive l'inizio pos e firma alla fine di un file di tipo exe

Dovrai leggere la documentazione per ciascuna delle tue lingue per comprenderne le regole di citazione e la sintassi generale.

Il tuo esempio

Il livello più interno del tuo esempio è un awk programma.

{print $1}

Lo incorporerai in una riga di comando della shell:

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

Dobbiamo proteggere (almeno) lo spazio e il $ nel awk programma. La scelta più ovvia è usare virgolette singole nella shell attorno all'intero programma.

  • '{print $1}'

Ci sono altre scelte però:

  • {print $1} esci direttamente dallo spazio e $
  • {print' $'1} virgolette singole solo lo spazio e $
  • "{print $1}" virgolette il tutto ed esci da $
  • {print" $"1} virgolette solo lo spazio e $
    Questo potrebbe piegare un po' le regole (senza caratteri di escape $ alla fine di una stringa tra virgolette è letterale), ma sembra funzionare nella maggior parte delle shell.

Se il programma utilizzava una virgola tra le parentesi graffe aperte e chiuse, avremmo anche bisogno di citare o sfuggire alla virgola o alle parentesi graffe per evitare "l'espansione delle parentesi graffe" in alcune shell.

Scegliamo '{print $1}' e incorporalo nel resto del “codice” della shell:

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

Successivamente, volevi eseguirlo tramite su e sudo .

sudo su user -c …

su user -c … è proprio come some-shell -c … (tranne per l'esecuzione con un altro UID), quindi su aggiunge solo un altro livello di shell. sudo non interpreta i suoi argomenti, quindi non aggiunge livelli di virgolette.

Abbiamo bisogno di un altro livello di shell per la nostra stringa di comando. Possiamo scegliere di nuovo le virgolette singole, ma dobbiamo dare una gestione speciale alle virgolette singole esistenti. Il solito modo si presenta così:

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

Ci sono quattro stringhe qui che la shell interpreterà e concatenerà:la prima stringa tra virgolette singole (pgrep … awk ), una virgoletta singola con escape, la virgoletta singola awk programma, un'altra virgoletta singola con escape.

Ci sono, ovviamente, molte alternative:

  • pgrep -fl java | grep -i datanode | awk '{print $1} sfuggire a tutto ciò che è importante
  • pgrep -fl java|grep -i datanode|awk '{print$1} lo stesso, ma senza spazi bianchi superflui (anche nel awk programma!)
  • "pgrep -fl java | grep -i datanode | awk '{print $1}'" virgolette il tutto, esci da $
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'" la tua variazione; un po' più lungo del solito a causa dell'utilizzo di virgolette doppie (due caratteri) invece di escape (un carattere)

L'utilizzo di virgolette diverse nel primo livello consente altre variazioni a questo livello:

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

Incorporando la prima variazione nel sudo /*su* riga di comando fornisce questo:

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

Potresti usare la stessa stringa in qualsiasi altro contesto a livello di shell singolo (ad es. ssh host … ).

Successivamente, hai aggiunto un livello di ssh in cima. Questo è effettivamente un altro livello di shell:ssh non interpreta il comando stesso, ma lo consegna a una shell sull'estremità remota (tramite (ad es.) sh -c … ) e quella shell interpreta la stringa.

ssh host …

Il processo è lo stesso:prendi la stringa, scegli un metodo di citazione, usalo, incorporalo.

Usando di nuovo le virgolette singole:

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

Ora ci sono undici stringhe che vengono interpretate e concatenate:'sudo su user -c ' , virgoletta singola con escape, 'pgrep … awk ' , virgolette singole con escape, barra rovesciata con escape, due virgolette singole con escape, le virgolette singole awk programma, una virgoletta singola con escape, una barra rovesciata con escape e una virgoletta singola con escape finale.

Relazionato:Le scatole GNOME si avviano da ISO dopo aver installato un sistema operativo?

Il modulo finale si presenta così:

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

Questo è un po' ingombrante da digitare a mano, ma la natura letterale delle virgolette singole della shell rende facile automatizzare una leggera variazione:

#!/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. Come limitare l'accesso SSH per l'utente con LShell (shell limitata)

  2. Ssh:come rifiutare un processo in esecuzione e associarlo a una nuova shell dello schermo?

  3. Ssh:limitare un utente Ssh/scp/sftp a una directory?

  4. Autorizzazioni Ssh e Home Directory?

  5. Nozioni di base su utenti e database MySQL

Utilizzo di Secure Shell (SSH) per l'accesso e Secure Copy (SCP) per il trasferimento dei dati su Linux

Scripting della shell Parte 2:accettazione degli input ed esecuzione dell'aritmetica della shell

Nozioni di base su Linux:come creare e installare chiavi SSH sulla shell

Tunneling e proxy SSH

copy_to_user() e copy_from_user() per il tipo di dati di base

Abilita l'accesso alla shell SSH ma disabilita l'accesso SFTP