Come posso usare su per eseguire il resto dello script bash come quell'utente?


126

Ho scritto uno script che prende, come argomento, una stringa che è una concatenazione di un nome utente e di un progetto. Lo script dovrebbe passare (su) al nome utente, cd in una directory specifica in base alla stringa del progetto.

Fondamentalmente voglio fare:

su $USERNAME;  
cd /home/$USERNAME/$PROJECT;  
svn update;  

Il problema è che una volta che faccio un su ... aspetta lì. Il che ha senso poiché il flusso di esecuzione è passato al passaggio all'utente. Una volta che esco, il resto delle cose viene eseguito ma non funziona come desiderato.

Ho anteposto su al comando svn ma il comando non è riuscito (cioè non ha aggiornato svn nella directory desiderata).

Come scrivo uno script che consenta all'utente di cambiare utente e invocare svn (tra le altre cose)?

Risposte:


86

Il trucco sta nell'usare il comando "sudo" invece di "su"

Potrebbe essere necessario aggiungerlo

username1 ALL=(username2) NOPASSWD: /path/to/svn

al tuo file / etc / sudoers

e cambia il tuo script in:

sudo -u username2 -H sh -c "cd /home/$USERNAME/$PROJECT; svn update" 

Dove nomeutente2 è l'utente con cui si desidera eseguire il comando SVN e nomeutente1 è l'utente che esegue lo script.

Se hai bisogno di più utenti per eseguire questo script, usa a %groupnameinvece di nomeutente1


Ho un problema simile, ma volevo correre chshper gli altri utenti. Il mio problema è elencato qui su stackoverflow.com/q/15307289/80353 Come adattare la tua risposta alla mia situazione?
Kim Stacks

L'ho fatto, ma mi ha comunque chiesto una password.
Hippyjim

@Hippyjim sei sicuro di aver capito bene i nomi utente?
Kimvais

1
L'ho fatto - si scopre che dovevo anche consentire l'uso di / bin / bash.
Hippyjim

3
Sia che tu usi sudoo susia di secondaria importanza, però sudoè molto più sicuro e conveniente.
tripleee

105

Molto più semplice: usa sudoper eseguire una shell e usa un heredoc per alimentare i suoi comandi.

#!/usr/bin/env bash
whoami
sudo -i -u someuser bash << EOF
echo "In"
whoami
EOF
echo "Out"
whoami

(risposta originariamente su SuperUser )


5
Questa risposta ha funzionato meglio. Raccomando di usare l'opzione -iper ottenere someuserl'ambiente previsto.
not2savvy

2
sudopuò essere conveniente ma non va bene se non è pronto per l'uso (come su AIX). su -c 'commands'è la risposta corretta.
Tricky

1
Soluzione epica!
3bdalla

Come rendere disponibili le variabili nell'ambito di herdoc?
Dotslashlu

su AIX genera questo errore ksh: sh: 0403-006 Autorizzazione di esecuzione negata.
AhmedRana

54

Utilizzare uno script come il seguente per eseguire il resto o parte dello script con un altro utente:

#!/bin/sh

id

exec sudo -u transmission /bin/sh - << eof

id

eof

11
Potresti voler usare "sudo -i -u ..." per assicurarti che cose come $ HOME siano impostate correttamente.
Bryan Larsen

Scusa per la domanda di noob, ma che cos'è un ID qui?
Nitin Jadhav

1
@NitinJadhav, l'ha usato qui solo per mostrare l'ID dell'utente corrente, l'ID di root è 0, quindi il primo ID ti mostrerà un numero, ma il secondo mostrerà sicuramente 0 (perché il secondo è stato eseguito all'interno un blocco gestito da root). È possibile utilizzare l'utente al whoamiposto del idquale restituirà il nome al posto dell'ID
Mohammed Noureldin

@MohammedNoureldin Thanks!
Nitin Jadhav

sudopuò essere conveniente ma non va bene se non è pronto per l'uso (come su AIX). su -c 'commands'è la risposta corretta.
Tricky

52

È necessario eseguire tutti i comandi di utenti diversi come il proprio script. Se è solo uno o pochi comandi, inline dovrebbe funzionare. Se sono presenti molti comandi, probabilmente è meglio spostarli nel loro file.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 

4
Questa è l'unica risposta corretta. Il sudo non è necessario per questo.
helvete

1
Potrebbe essere necessario fornire anche shell su -s /bin/bash.
mixel

migliore soluzione per utenti root
rfinz

La migliore risposta per coloro che non hanno installato sudo per impostazione predefinita (ti sto guardando AIX)
Tricky

46

Ecco ancora un altro approccio, che era più conveniente nel mio caso (volevo solo eliminare i privilegi di root e fare il resto del mio script da un utente con restrizioni): puoi fare in modo che lo script si riavvii da solo dall'utente corretto. Supponiamo che inizialmente venga eseguito come root. Quindi sarà simile a questo:

#!/bin/bash
if [ $UID -eq 0 ]; then
  user=$1
  dir=$2
  shift 2     # if you need some other parameters
  cd "$dir"
  exec su "$user" "$0" -- "$@"
  # nothing will be executed beyond that line,
  # because exec replaces running process with the new one
fi

echo "This will be run from user $UID"
...

6
Mi chiedo perché questo non abbia un punteggio più alto. Risolve al meglio la domanda originale mantenendo tutto in bash.
SirVer

Oppure runuser -u $user -- "$@", come affermato in su (1)
cghislai

1
exec su "$user" "$0" -- "$@"
macieksk

1
Grazie @macieksk, bella cattura. Si aggiornerà. L' --è veramente utile.
MarSoft

1
L'approccio presentato è il migliore di tutti ed è completo. Tutto è scritto all'interno di un singolo script e tutti usano bash. In effetti, questo script viene eseguito due volte. Al primo test dello script è root, quindi prepara l'ambiente e cambia utente con su. Ma fallo con il comando exec, quindi c'è solo una singola istanza di script. Con il secondo ciclo, la frase if / fi è ommited, quindi lo script esegue direttamente l'azione preparata. In questo esempio è eco. Tutti gli altri approcci risolvono il problema solo parzialmente. Ovviamente questo script può essere eseguito sotto sh non bash, ma dobbiamo testare la variabile speciale $ HOME, non $ UID
Znik

7

Usa sudoinvece

EDIT : Come ha sottolineato Douglas, non puoi usare cdinsudo poiché non è un comando esterno . Devi eseguire i comandi in una subshell per fare il cdlavoro.

sudo -u $USERNAME -H sh -c "cd ~/$PROJECT; svn update"

sudo -u $USERNAME -H cd ~/$PROJECT
sudo -u $USERNAME svn update

È possibile che ti venga chiesto di inserire la password di quell'utente, ma solo una volta.


Tuttavia, non funzionerà: il cd andrà perso al termine dell'esecuzione del primo sudo.
Douglas Leeder

In realtà, non puoi nemmeno chiamare cd direttamente perché non è un comando esterno .
iamamac

sudopuò essere conveniente ma non va bene se non è pronto per l'uso (come su AIX). su -c 'commands'è la risposta corretta.
Tricky

6

Non è possibile cambiare utente all'interno di uno script di shell. Le soluzioni alternative che utilizzano sudo descritte in altre risposte sono probabilmente la soluzione migliore.

Se sei abbastanza matto da eseguire gli script Perl come root, puoi farlo con le $< $( $> $) variabili che contengono uid / gid reale / effettivo, ad esempio:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');

-1 as It È possibile con sudo per ottenere temporaneamente i diritti di altri utenti.
Kimvais

4
Non è possibile nel senso che l'utente viene eseguito dallo script di shell in quanto non può essere modificato (che è ciò che veniva posta dalla domanda originale). Invocare altri processi con sudo non cambia la persona con cui viene eseguito lo script stesso.
P-Nuts

2

Questo ha funzionato per me

Ho diviso il mio "provisioning" dal mio "avvio".

 # Configure everything else ready to run 
  config.vm.provision :shell, path: "provision.sh"
  config.vm.provision :shell, path: "start_env.sh", run: "always"

quindi nel mio start_env.sh

#!/usr/bin/env bash

echo "Starting Server Env"
#java -jar /usr/lib/node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-2.40.0.jar  &
#(cd /vagrant_projects/myproj && sudo -u vagrant -H sh -c "nohup npm install 0<&- &>/dev/null &;bower install 0<&- &>/dev/null &")
cd /vagrant_projects/myproj
nohup grunt connect:server:keepalive 0<&- &>/dev/null &
nohup apimocker -c /vagrant_projects/myproj/mock_api_data/config.json 0<&- &>/dev/null &

-2

Ispirato dall'idea di @ MarSoft ma ho cambiato le linee in questo modo:

USERNAME='desireduser'
COMMAND=$0
COMMANDARGS="$(printf " %q" "${@}")"
if [ $(whoami) != "$USERNAME" ]; then
  exec sudo -E su $USERNAME -c "/usr/bin/bash -l $COMMAND $COMMANDARGS"
  exit
fi

Ho usato sudoper consentire una password meno l'esecuzione dello script. Se desideri inserire una password per l'utente, rimuovi il file sudo. Se non hai bisogno delle variabili d'ambiente, rimuovi-E da sudo.

Le /usr/bin/bash -lassicura che le profile.dscript vengono eseguiti per un ambiente inizializzato.


sudopuò essere conveniente ma non va bene se non è pronto per l'uso (come su AIX). su -c 'commands'è la risposta corretta.
Tricky

@Tricky Forse leggi la risposta completa, suggerisce già di rimuovere sudo. In effetti, sudo non è così semplice, in molti casi è necessaria anche sudo -Euna voce di configurazione in sudoers.d per consentire l'esecuzione senza tty con !requiretty. Ma ci sono molti casi in cui sudo è necessario per gli script chiamati automatici, in cui una finestra di dialogo per la password potrebbe interferire. Quindi non lo rimuoverei da una soluzione standard.
Trendfischer

Il codice in questione è completamente bacato: interromperà i nomi degli script o gli argomenti con spazi; tieni presente che l'inserimento "$@"di una stringa significa che gli argomenti oltre il primo vengono aggiunti in una stringa separata, non inclusa -cnell'argomento. Se vuoi renderlo sicuro, potresti printf -v arg_q '%q ' "$0" "$@"quindi usaresu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy

@CharlesDuffy Hai ragione! Ho semplificato troppo il mio script di produzione per questa risposta :-( La semplice aggiunta di un COMMANDARGS=$@risolve il problema con -c. Gli argomenti con spazi non erano un problema prima, ma ho implementato il tuo buon input. Dovevo solo fare alcuni esperimenti per farlo funzionare. ho modificato la domanda e spero di non aver incollato un altro bug. Grazie per il tuo commento, umiliante ma necessario.
Trendfischer
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.