Como uso o su para executar o restante do script bash como esse usuário?

126

Eu escrevi um script que usa, como argumento, uma string que é uma concatenação de um nome de usuário e um projeto. O script deve mudar (su) para o nome de usuário, cd para um diretório específico com base na sequência do projeto.

Eu basicamente quero fazer:

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

O problema é que uma vez que eu faço um su ... ele apenas espera lá. O que faz sentido desde que o fluxo de execução passou para a mudança para o usuário. Depois que eu saio, o restante das coisas é executado, mas não funciona como desejado.

Anexei su ao comando svn, mas o comando falhou (ou seja, não atualizou o svn no diretório desejado).

Como escrevo um script que permite ao usuário alternar entre usuários e chamar svn (entre outras coisas)?

Avery
fonte

Respostas:

86

O truque é usar o comando "sudo" em vez de "su"

Pode ser necessário adicionar este

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

para o seu arquivo / etc / sudoers

e mude seu script para:

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

Onde username2 é o usuário para o qual você deseja executar o comando SVN e username1 é o usuário que executa o script.

Se você precisar de vários usuários para executar esse script, use a em %groupnamevez do nome de usuário1

Kimvais
fonte
Eu tenho um problema semelhante, mas queria executar chshpara os outros usuários. Meu problema está listado aqui em stackoverflow.com/q/15307289/80353 Como adapto sua resposta à minha situação?
precisa
Eu fiz isso - mas ainda me pedia uma senha.
Hippyjim 19/10/2013
@Hippyjim, você tem certeza de que os nomes de usuário estão corretos?
19413 Kimvais
1
Eu fiz - acontece que eu também precisava permitir o uso de / bin / bash.
Hippyjim
3
Se você usa sudoou sué de importância secundária, sudoé muito mais seguro e conveniente.
tripleee
105

Muito mais simples: use sudopara executar um shell e use um heredoc para alimentá-lo.

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

(resposta originalmente no SuperUser )

Dan Dascalescu
fonte
5
Essa resposta funcionou melhor. Eu recomendo usar a opção -ipara obter someusero ambiente esperado.
not2savvy
2
sudopode ser conveniente, mas não é bom se não estiver pronto para uso (como no AIX). su -c 'commands'é a resposta correta.
Tricky
1
Solução épica!
precisa saber é o seguinte
Como disponibilizar variáveis ​​no escopo do herdoc?
dotslashlu
no AIX, gera este erro ksh: sh: 0403-006 Permissão de execução negada.
AhmedRana 6/08/19
54

Use um script como o seguinte para executar o restante ou parte do script em outro usuário:

#!/bin/sh

id

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

id

eof
Walder
fonte
11
Você pode usar "sudo -i -u ..." para garantir que itens como $ HOME sejam definidos corretamente.
Bryan Larsen
Desculpe pela pergunta noob, mas o que é um id aqui?
Nitin Jadhav
1
@NitinJadhav, ele usou aqui apenas para mostrar o ID do usuário atual, o ID da raiz é 0, então o primeiro ID mostrará um número, mas o segundo mostrará definitivamente 0 (porque o segundo foi executado dentro um bloco executado pela raiz). Você pode usar o usuário em whoamivez do idqual retornará o nome em vez do id
Mohammed Noureldin
@MohammedNoureldin Thanks!
Nitin Jadhav
sudopode ser conveniente, mas não é bom se não estiver pronto para uso (como no AIX). su -c 'commands'é a resposta correta.
Tricky
52

Você precisa executar todos os comandos de diferentes usuários como seu próprio script. Se for apenas um ou alguns comandos, o inline deve funcionar. Se houver muitos comandos, provavelmente é melhor movê-los para seu próprio arquivo.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Douglas Leeder
fonte
4
Esta é a única resposta correta. O sudo não é necessário para isso.
helvete
1
Você também pode precisar fornecer shell su -s /bin/bash.
mixel
melhor solução para usuários root
rfinz
Melhor resposta para quem não possui o sudo instalado por padrão (estou olhando para você AIX)
Tricky
46

Aqui está outra abordagem, que era mais conveniente no meu caso (eu só queria eliminar privilégios de root e fazer o resto do meu script de usuário restrito): você pode fazer com que o script seja reiniciado automaticamente a partir do usuário correto. Vamos supor que ele seja executado como root inicialmente. Então ficará assim:

#!/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
fonte
6
Eu me pergunto por que isso não tem uma classificação mais alta. Ele está resolvendo a pergunta original da melhor forma, mantendo tudo em ordem.
precisa saber é
Ou runuser -u $user -- "$@", como diz su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
macieksk
1
Obrigado @macieksk, boa captura. Atualizará. O --é realmente útil.
MarSoft
1
A abordagem apresentada é a melhor de todas e está completa. Tudo é escrito dentro de um único script, e todos usam bash. Efetivamente, esse script é executado duas vezes. No primeiro teste de script, ele é root, prepara o ambiente e altera o usuário por su. Mas faça isso com o comando exec, pois há apenas uma instância de script única. No segundo loop, frase se / fi for omitida, o script executará diretamente a ação preparada. Neste exemplo, é eco. Todas as outras abordagens resolvem o problema apenas parcialmente. Obviamente, esse script pode ser executado sob sh not bash, mas devemos testar a variável especial $ HOME, não $ UID
Znik
7

Use em sudovez disso

Edição : Como apontou Douglas, você não pode usar cd, sudopois não é um comando externo . Você precisa executar os comandos em um subshell para fazer o cdtrabalho.

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

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

Você pode ser solicitado a inserir a senha desse usuário, mas apenas uma vez.

iamamac
fonte
Porém, isso não funcionará - o CD será perdido após a execução do primeiro sudo.
Douglas Leeder 01/01
Na verdade, você não pode nem chamar o cd diretamente, porque não é um comando externo .
iamamac
sudopode ser conveniente, mas não é bom se não estiver pronto para uso (como no AIX). su -c 'commands'é a resposta correta.
Tricky
6

Não é possível alterar o usuário dentro de um script de shell. Soluções alternativas usando o sudo descritas em outras respostas provavelmente são sua melhor aposta.

Se você é louco o suficiente para executar scripts perl como root, pode fazer isso com as $< $( $> $) variáveis ​​que contêm uid / gid real / eficaz, por exemplo:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
P-Nuts
fonte
-1, pois é possível com o sudo obter temporariamente os direitos de outros usuários.
Kimvais 01/01/10
4
Não é possível no sentido em que o usuário que executa o script shell não possa ser alterado (que é o que a pergunta original fez). Invocar outros processos com o sudo não altera quem o próprio script está sendo executado.
P-Nuts
2

Isso funcionou para mim

Dividi meu "provisionamento" da minha "inicialização".

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

então no meu 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
fonte
-2

Inspirado na idéia da @ MarSoft, mas mudei as linhas da seguinte maneira:

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

Eu usei sudopara permitir uma senha menos execução do script. Se você deseja inserir uma senha para o usuário, remova o sudo. Se você não precisar das variáveis ​​de ambiente, remova-E do sudo.

Os /usr/bin/bash -lgarante, que os profile.dscripts são executados em um ambiente inicializado.

Trendfischer
fonte
sudopode ser conveniente, mas não é bom se não estiver pronto para uso (como no AIX). su -c 'commands'é a resposta correta.
Tricky
@ Tricky Talvez leia a resposta completa, já sugere a remoção sudo. De fato, o sudo não é tão simples, em muitos casos você precisa mesmo sudo -Ee uma entrada de configuração no sudoers.d para permitir a execução sem o tty with !requiretty. Mas existem muitos casos em que o sudo é necessário para scripts chamados automaticamente, em que um diálogo de senha pode interferir. Portanto, eu não o removeria de uma solução padrão.
Trendfischer 23/03/19
O código em questão é totalmente incorreto - ele quebrará nomes ou argumentos de scripts com espaços; lembre-se de que colocar "$@"dentro de uma string significa que os argumentos anteriores ao primeiro são anexados a uma string separada, não incluída no -cargumento. Se você quiser torná-lo seguro, você pode printf -v arg_q '%q ' "$0" "$@"e, em seguida, usarsu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy
@CharlesDuffy Você está certo! Simplifiquei muito meu script de produção para esta resposta :-( Apenas adicionar um COMMANDARGS=$@resolve o problema com -c. Argumentos com espaços não eram um problema antes, mas eu implementei sua boa entrada. Eu só tinha que fazer algumas experiências para fazê-lo funcionar. editei a pergunta e espero não ter colado outro bug. Obrigado pelo seu comentário, humilhante, mas necessário.
Trendfischer