¿Cómo uso su para ejecutar el resto del script bash como ese usuario?

126

He escrito un script que toma, como argumento, una cadena que es una concatenación de un nombre de usuario y un proyecto. Se supone que el script cambia (su) al nombre de usuario, cd a un directorio específico basado en la cadena del proyecto.

Básicamente quiero hacer:

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

El problema es que una vez que hago un su ... solo espera allí. Lo cual tiene sentido ya que el flujo de ejecución ha pasado a cambiar al usuario. Una vez que salgo, el resto de las cosas se ejecutan pero no funciona como se desea.

Prepuse su al comando svn pero el comando falló (es decir, no actualizó svn en el directorio deseado).

¿Cómo escribo un script que permita al usuario cambiar de usuario e invocar svn (entre otras cosas)?

Avery
fuente

Respuestas:

86

El truco es usar el comando "sudo" en lugar de "su"

Es posible que deba agregar esto

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

a su archivo / etc / sudoers

y cambia tu guión a:

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

Donde username2 es el usuario con el que desea ejecutar el comando SVN y username1 es el usuario que ejecuta el script.

Si necesita varios usuarios para ejecutar este script, use un en %groupnamelugar del nombre de usuario1

Kimvais
fuente
Tengo un problema similar, pero quería correr chshpara los otros usuarios. Mi problema se enumera aquí en stackoverflow.com/q/15307289/80353 ¿Cómo adapto su respuesta en mi situación?
Kim Stacks
Hice esto, pero aún así me pidió una contraseña.
Hippyjim
@Hippyjim, ¿estás seguro de que tienes los nombres de usuario correctamente?
Kimvais
1
Lo hice, resulta que también necesitaba permitir el uso de / bin / bash.
Hippyjim
3
Si lo usa sudoo sues de importancia secundaria, sudoes mucho más seguro y conveniente.
tripleee
105

Mucho más simple: use sudopara ejecutar un shell y use un heredoc para alimentar sus comandos.

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

(respuesta original en SuperUser )

Dan Dascalescu
fuente
55
Esta respuesta funcionó mejor. Recomiendo usar la opción -ipara obtener someuserel entorno esperado.
not2savvy
2
sudopuede ser conveniente, pero no es bueno si no está listo para usar (como en AIX). su -c 'commands'Es la respuesta correcta.
Tricky
1
¡Solución épica!
3bdalla
¿Cómo hacer que las variables estén disponibles en el ámbito de herdoc?
dotslashlu
en AIX arroja este error ksh: sh: 0403-006 Permiso de ejecución denegado.
AhmedRana
54

Use un script como el siguiente para ejecutar el resto o parte del script bajo otro usuario:

#!/bin/sh

id

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

id

eof
Walder
fuente
11
Es posible que desee utilizar "sudo -i -u ..." para asegurarse de que cosas como $ HOME estén configuradas correctamente.
Bryan Larsen
Perdón por la pregunta novata, pero ¿cuál es una identificación aquí?
Nitin Jadhav
1
@NitinJadhav, lo usó aquí solo para mostrar la identificación del usuario actual, la identificación de la raíz es 0, por lo que la primera identificación le mostrará algún número, pero el segundo definitivamente mostrará 0 (porque el segundo se ejecutó dentro un bloque ejecutado por root). Puede usar un usuario en whoamilugar del idcual devolverá el nombre en lugar de la identificación
Mohammed Noureldin
@MohammedNoureldin ¡Gracias!
Nitin Jadhav
sudopuede ser conveniente, pero no es bueno si no está listo para usar (como en AIX). su -c 'commands'Es la respuesta correcta.
Tricky
52

Debe ejecutar todos los comandos de diferentes usuarios como su propio script. Si es solo uno o algunos comandos, entonces en línea debería funcionar. Si se trata de muchos comandos, probablemente sea mejor moverlos a su propio archivo.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Douglas Leeder
fuente
44
Esta es la única respuesta correcta. El sudo no es necesario para esto.
helvete
1
Es posible que también necesite proporcionar shell su -s /bin/bash.
mixel
mejor solución para usuarios root
rfinz
La mejor respuesta para aquellos que no tienen sudo instalado por defecto (te estoy mirando AIX)
Tricky
46

Aquí hay otro enfoque, que fue más conveniente en mi caso (solo quería eliminar los privilegios de root y hacer el resto de mi script desde un usuario restringido): puede hacer que el script se reinicie desde el usuario correcto. Supongamos que se ejecuta como root inicialmente. Entonces se verá así:

#!/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"
...
MarSoft
fuente
66
Me pregunto por qué esto no tiene una calificación más alta. Está resolviendo la pregunta original de la mejor manera manteniendo todo en bash.
SirVer
O runuser -u $user -- "$@", como se indica en su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
macieksk
1
Gracias @macieksk, buena captura. Actualizará. El --es realmente útil.
MarSoft
1
El enfoque presentado es el mejor de todos y está completo. Todo está escrito dentro de un solo script, y todos usan bash. Efectivamente, este script se ejecuta dos veces. En la primera prueba de script es root, luego prepara el entorno y cambia el usuario por su. Pero hazlo con el comando exec, entonces solo hay una única instancia de script. Con el segundo bucle, se omite la frase if / fi, luego el script realiza directamente la acción preparada. En este ejemplo es echo. Todos los demás enfoques resuelven el problema solo parcialmente. Por supuesto, este script se puede ejecutar bajo sh not bash, pero debemos probar la variable especial $ HOME, no $ UID
Znik
7

Utilizar en su sudolugar

EDITAR : Como Douglas señaló, no se puede utilizar cden sudopuesto que no es una externa de comando. Tienes que ejecutar los comandos en una subshell para hacer el cdtrabajo.

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

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

Se le puede pedir que ingrese la contraseña de ese usuario, pero solo una vez.

iamamac
fuente
Sin embargo, eso no funcionará: el CD se perderá después de que el primer sudo haya terminado de ejecutarse.
Douglas Leeder
En realidad, ni siquiera puedes llamar a cd directamente porque no es un comando externo .
iamamac
sudopuede ser conveniente, pero no es bueno si no está listo para usar (como en AIX). su -c 'commands'Es la respuesta correcta.
Tricky
6

No es posible cambiar de usuario dentro de un script de shell. Las soluciones alternativas que usan sudo descritas en otras respuestas son probablemente su mejor opción.

Si está lo suficientemente loco como para ejecutar scripts perl como root, puede hacerlo con las $< $( $> $) variables que contienen uid / gid real / efectivo, por ejemplo:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
Tuercas P
fuente
-1 como ES posible con sudo para obtener temporalmente los derechos de otros usuarios.
Kimvais
44
No es posible en el sentido de que el usuario que ejecuta el script de shell en sí mismo no se pueda cambiar (que es lo que se hizo la pregunta original). Invocar otros procesos con sudo no cambia a quién se ejecuta el script.
P-Nuts
2

Esto funciono para mi

Separé mi "aprovisionamiento" de mi "inicio".

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

entonces en mi 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 &
httpete
fuente
-2

Inspirado por la idea de @ MarSoft pero cambié las líneas de la siguiente manera:

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

Solía sudopermitir una contraseña menos ejecución del script. Si desea ingresar una contraseña para el usuario, elimine el sudo. Si no necesita las variables de entorno, elimine -Ede sudo.

El /usr/bin/bash -lasegura, que los profile.dscripts se ejecutan para un entorno inicializado.

Trendfischer
fuente
sudopuede ser conveniente, pero no es bueno si no está listo para usar (como en AIX). su -c 'commands'Es la respuesta correcta.
Tricky
@Tricky Quizás lea la respuesta completa, ya sugiere eliminarla sudo. De hecho, sudo no es tan simple, en muchos casos necesita incluso sudo -Euna entrada de configuración en sudoers.d para permitir la ejecución sin tty con !requiretty. Pero hay muchos casos en los que sudo es necesario para secuencias de comandos llamadas automáticas, donde un diálogo de contraseña puede interferir. Por lo tanto, no lo eliminaría de una solución estándar.
Trendfischer
El código en cuestión es completamente defectuoso: romperá los nombres de guiones o argumentos con espacios; tenga en cuenta que poner "$@"dentro de una cadena significa que los argumentos más allá de la primera se agregan en una cadena separada, no incluida en el -cargumento. Si usted quiere que sea seguro, es posible que printf -v arg_q '%q ' "$0" "$@"a continuación, utilizarsu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy
@CharlesDuffy ¡Tienes razón! Simplifiqué demasiado mi secuencia de comandos de producción para esta respuesta :-( Simplemente agregando un COMMANDARGS=$@resuelve el problema con -c. Los argumentos con espacios no eran un problema antes, pero implementé su buen aporte. Solo tuve que hacer algunos experimentos para que funcionara. He editado la pregunta y espero no haber pegado otro error. Gracias por tu comentario, humillante pero necesario.
Trendfischer