GNU/Linux >> Linux Esercitazione >  >> Linux

Perché una shell di accesso "sudo -i" interrompe un argomento della stringa di comando Here-doc?

Nella sequenza di cinque comandi di seguito, tutti dipendono da virgolette singole per trasferire la possibile sostituzione di variabili al denominato bash shell anziché la shell chiamante. L'utente chiamante è xx , ma la shell chiamata verrà eseguita come utente yy . Il primo comando sostituisce $HOME con il valore della shell chiamante perché la shell chiamata non è una shell di login. Il secondo comando sostituisce il valore di $HOME caricato da una shell di login, quindi è il valore che appartiene all'utente yy . Il terzo comando non si basa su un valore $HOME e crea un file nella directory home ipotizzata dell'utente yy .

Perché il quarto comando non riesce? L'intenzione è che scriva lo stesso file, ma basandosi sulla variabile $HOME appartenente all'utente yy per assicurarsi che finisca effettivamente nella sua home directory. Non capisco perché una shell di accesso interrompa il comportamento di un comando here-doc passato come stringa statica con virgolette singole. Il fallimento del quinto comando verifica che questo problema non riguarda la sostituzione delle variabili.

[email protected] ~ $ sudo -u yy bash -c 'echo HOME=$HOME'
HOME=/home/xx
[email protected] ~ $ sudo -iu yy bash -c 'echo HOME=$HOME'
HOME=/home/yy
[email protected] ~ $ sudo -u yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
[email protected] ~ $ sudo -iu yy bash -c 'cat > $HOME/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')
[email protected] ~ $ sudo -iu yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')

Questi comandi sono stati immessi su un sistema Linux Mint 18.3 Cinnamon a 64 bit, basato su Ubuntu 16.04 (Xenial Xerus).

Aggiornamento: L'aspetto qui-doc sta solo offuscando il problema. Ecco una semplificazione del problema:

$ sudo bash -c 'echo 1
> echo 2'
1
2
$ sudo -i bash -c 'echo 1
> echo 2'
1echo 2

Perché il primo di questi due comandi preserva l'interruzione di riga e il secondo no? sudo è comune a entrambi i comandi, ma sembra sfuggire/filtrare/interpolare in modo diverso a seconda di nient'altro che l'opzione "-i".

Risposta accettata:

La documentazione per -i afferma:

Il -i (simula login iniziale) esegue la shell specificata dalla voce del database delle password dell'utente di destinazione come shell di login. Ciò significa che i file di risorse specifici per l'accesso come .profile o .login verranno letti dalla shell. Se viene specificato un comando, questo viene passato alla shell per l'esecuzione tramite l'opzione -c della shell.

Cioè, esegue davvero la shell di accesso dell'utente, quindi passa qualsiasi comando tu abbia dato sudo ad esso usando -cdiversamente da cosa sudo cmd arg arg di solito fa senza il -i opzione. Normalmente, sudo usa solo uno dei exec* funziona direttamente per avviare il processo stesso, senza shell intermedia e tutti gli argomenti passati esattamente come sono.

Con -i , configura l'ambiente, esegue la shell dell'utente come shell di login e ricostruisce il comando che hai chiesto di eseguire come argomento per bash -c . Nel tuo caso, esegue (diciamo, approssimativamente) /bin/bash -c "bash -c ' ... '" (immagina di citare che funzioni).

Il problema sta nel modo in cui sudo trasforma il comando che hai scritto in qualcosa che -c può occuparsi di , spiegato nella sezione successiva. L'ultima sezione presenta alcune possibili soluzioni, e in mezzo c'è qualche tecnica di debug e verifica,

Perché succede?

Quando si passa il comando a -c , ha bisogno di un po' di pre-elaborazione per farlo fare la cosa giusta, che sudo fa prima di eseguire la shell. Ad esempio, se il tuo comando è:

sudo -iu yy echo 'three   spaces'

quindi è necessario eseguire l'escape di quegli spazi affinché il comando significhi la stessa cosa (cioè, affinché il singolo argomento non venga diviso in due parole). Ciò che finisce per essere eseguito è:

/bin/bash -c 'echo three   spaces'

Iniziamo con il tuo comando semplificato:

sudo bash -c 'echo 1
echo 2'

In questo caso, sudo cambia utente, quindi esegue execvp("bash", ["bash", "-c", "echo 1necho 2"]) (per una sintassi letterale di matrice inventata).

Correlati:come ottenere il completamento di bash per gli alias di comando?

Con -i :

sudo -i bash -c 'echo 1
echo 2'

invece cambia utente, quindi esegue execv("/bin/bash", ["-bash", "-c", "bash -c echo\ 1\necho\ 2"]) , dove \ equivale a un letterale e n è un'interruzione di riga. È sfuggito agli spazi e alla nuova riga nel comando principale facendoli precedere da barre inverse.

Cioè, c'è una shell di login esterna, che ha due argomenti:-c e l'intero comando, ricostruito in una forma che la shell dovrebbe comprendere correttamente. Sfortunatamente, non è così. Il bash interno il comando alla fine tenta di eseguire:

echo 1
echo 2

dove la prima riga fisica termina con una continuazione di riga (barra rovesciata seguita da nuova riga), che viene eliminata completamente. La linea logica è quindi solo echo 1echo 2 , che non fa quello che volevi.

C'è un argomento secondo cui questo è un difetto in sudo sta scappando, dato il comportamento standard delle coppie barra rovesciata-nuova riga. Penso che dovrebbe essere sicuro lasciarli senza scampo qui.

Lo stesso accade per il tuo comando con un here-document. Funziona come, all'incirca:

/bin/bash -c 'bash -c cat << "EOF"\012script-content\012EOF\012'

dove

Linux
  1. Perché "ls" richiede un processo separato per l'esecuzione?

  2. Scripting della shell Linux:numero esadecimale in stringa binaria

  3. Modifica della shell predefinita in Linux

  4. Perché il comando Linux wall non trasmette un argomento stringa?

  5. Perché Ctrl + V non incolla in Bash (shell Linux)?

Linux chsh Command Tutorial per principianti (5 esempi)

Perché $shlvl inizia al livello 2 nelle shell non di accesso ma al livello 1 nelle shell di accesso in Rhel 7?

Cosa significa la sintassi |&nel linguaggio della shell?

Perché popen() invoca una shell per eseguire un processo?

Perché eseguire un comando shell Linux con "&"?

Perché il comando sudo richiede molto tempo per essere eseguito?