Wie verwende ich su, um den Rest des Bash-Skripts als dieser Benutzer auszuführen?

126

Ich habe ein Skript geschrieben, das als Argument eine Zeichenfolge verwendet, die eine Verkettung eines Benutzernamens und eines Projekts darstellt. Das Skript soll (su) zum Benutzernamen wechseln, cd in ein bestimmtes Verzeichnis basierend auf der Projektzeichenfolge.

Ich möchte im Grunde tun:

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

Das Problem ist, dass wenn ich einmal eine Su mache ... es nur dort wartet. Dies ist sinnvoll, da der Ausführungsfluss auf den Wechsel zum Benutzer übergegangen ist. Sobald ich beende, werden die restlichen Dinge ausgeführt, aber es funktioniert nicht wie gewünscht.

Ich habe dem Befehl svn su vorangestellt, aber der Befehl ist fehlgeschlagen (dh er hat svn im gewünschten Verzeichnis nicht aktualisiert).

Wie schreibe ich ein Skript, mit dem der Benutzer den Benutzer wechseln und (unter anderem) svn aufrufen kann?

Avery
quelle

Antworten:

86

Der Trick besteht darin, den Befehl "sudo" anstelle von "su" zu verwenden.

Möglicherweise müssen Sie dies hinzufügen

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

in Ihre / etc / sudoers-Datei

und ändern Sie Ihr Skript in:

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

Wobei Benutzername2 der Benutzer ist, unter dem Sie den SVN-Befehl ausführen möchten, und Benutzername1 der Benutzer, der das Skript ausführt.

Wenn Sie mehrere Benutzer benötigen, um dieses Skript auszuführen, verwenden Sie a %groupnameanstelle des Benutzernamens1

Kimvais
quelle
Ich habe ein ähnliches Problem, wollte es aber chshfür die anderen Benutzer ausführen . Mein Problem ist hier unter stackoverflow.com/q/15307289/80353 aufgeführt. Wie passe ich Ihre Antwort an meine Situation an?
Kim Stacks
Ich habe das getan - aber es hat mich trotzdem nach einem Passwort gefragt.
Hippyjim
@Hippyjim bist du sicher, dass du die Benutzernamen richtig verstanden hast?
Kimvais
1
Ich habe es getan - es stellte sich heraus, dass ich auch die Verwendung von / bin / bash zulassen musste.
Hippyjim
3
Ob Sie verwenden sudooder suvon untergeordneter Bedeutung ist, sudoist jedoch viel sicherer und bequemer.
Tripleee
105

Viel einfacher: Verwenden Sie sudodiese Option , um eine Shell auszuführen, und verwenden Sie einen Heredoc , um die Befehle einzugeben .

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

(Antwort ursprünglich auf SuperUser )

Dan Dascalescu
quelle
5
Diese Antwort hat am besten funktioniert. Ich empfehle, die Option -izu verwenden, um someuserdie erwartete Umgebung zu erhalten.
not2savvy
2
sudomag praktisch sein, aber es ist nicht gut, wenn es nicht sofort einsatzbereit ist (wie unter AIX). su -c 'commands'ist die richtige Antwort.
Tricky
1
Epische Lösung!
3bdalla
Wie werden Variablen im Bereich von herdoc verfügbar gemacht?
Dotslashlu
Unter AIX wird der folgende Fehler ausgelöst: ksh: sh: 0403-006 Ausführungsberechtigung verweigert.
AhmedRana
54

Verwenden Sie ein Skript wie das folgende, um den Rest oder einen Teil des Skripts unter einem anderen Benutzer auszuführen:

#!/bin/sh

id

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

id

eof
Walder
quelle
11
Vielleicht möchten Sie "sudo -i -u ..." verwenden, um sicherzustellen, dass Dinge wie $ HOME richtig eingestellt sind.
Bryan Larsen
Entschuldigung für die Noob-Frage, aber was ist hier eine ID?
Nitin Jadhav
1
@NitinJadhav, er hat es hier nur verwendet, um die ID des aktuellen Benutzers anzuzeigen. Die ID von root ist 0, daher zeigt die erste ID eine Nummer an, während die zweite definitiv 0 anzeigt (da die zweite im Inneren ausgeführt wurde ein Block, der von root ausgeführt wird). Sie können Benutzer whoamianstelle iddessen den Namen anstelle der ID zurückgeben
Mohammed Noureldin
@ MohammedNoureldin Danke!
Nitin Jadhav
sudomag praktisch sein, aber es ist nicht gut, wenn es nicht sofort einsatzbereit ist (wie unter AIX). su -c 'commands'ist die richtige Antwort.
Tricky
52

Sie müssen alle Befehle für verschiedene Benutzer als eigenes Skript ausführen. Wenn es nur ein oder mehrere Befehle sind, sollte Inline funktionieren. Wenn es viele Befehle gibt, ist es wahrscheinlich am besten, sie in ihre eigene Datei zu verschieben.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Douglas Leeder
quelle
4
Dies ist die einzig richtige Antwort. Das Sudo ist dafür nicht notwendig.
Helvete
1
Möglicherweise müssen Sie auch Shell mit bereitstellen su -s /bin/bash.
Mixel
beste Lösung für Root-Benutzer
rfinz
Beste Antwort für diejenigen, die sudo nicht standardmäßig installiert haben (ich sehe dich AIX an)
Tricky
46

Hier ist noch ein weiterer Ansatz, der in meinem Fall bequemer war (ich wollte nur Root-Berechtigungen löschen und den Rest meines Skripts von einem eingeschränkten Benutzer ausführen): Sie können das Skript vom richtigen Benutzer aus neu starten lassen. Nehmen wir an, es wird zunächst als Root ausgeführt. Dann sieht es so aus:

#!/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
quelle
6
Ich frage mich, warum dies nicht höher bewertet wird. Es löst die ursprüngliche Frage am besten, während alles in Schach gehalten wird.
SirVer
Oder runuser -u $user -- "$@", wie in su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
Macieksk
1
Danke @macieksk, schöner Fang. Werde dich auf den neuesten Stand bringen. Das --ist wirklich nützlich.
MarSoft
1
Der vorgestellte Ansatz ist der beste von allen und vollständig. Alles ist in einem einzigen Skript geschrieben und alle verwenden bash. Tatsächlich wird dieses Skript zweimal ausgeführt. Beim ersten Skripttest handelt es sich um root, dann um die Umgebung vorzubereiten und den Benutzer durch su zu ändern. Aber machen Sie es mit dem Befehl exec, dann gibt es nur eine einzige Skriptinstanz. Bei der zweiten Schleife wird die Phrase if / fi weggelassen, und das Skript führt direkt die vorbereitete Aktion aus. In diesem Beispiel ist es Echo. Alle anderen Ansätze lösen das Problem nur teilweise. Natürlich kann dieses Skript unter sh not bash ausgeführt werden, aber wir müssen die spezielle Variable $ HOME testen, nicht $ UID
Znik
7

Verwenden Sie sudostattdessen

EDIT : Wie Douglas wies darauf hin, sie können nicht cdin sudoda es kein ist externer Befehl. Sie müssen die Befehle in einer Subshell ausführen, damit die cdArbeit funktioniert.

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

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

Möglicherweise werden Sie aufgefordert, das Kennwort dieses Benutzers einzugeben, jedoch nur einmal.

iamamac
quelle
Das wird aber nicht funktionieren - die CD geht verloren, nachdem der erste Sudo ausgeführt wurde.
Douglas Leeder
Tatsächlich können Sie cd nicht einmal direkt aufrufen, da es sich nicht um einen externen Befehl handelt.
Iamamac
sudomag praktisch sein, aber es ist nicht gut, wenn es nicht sofort einsatzbereit ist (wie unter AIX). su -c 'commands'ist die richtige Antwort.
Tricky
6

Es ist nicht möglich, den Benutzer innerhalb eines Shell-Skripts zu ändern. Problemumgehungen mit sudo, die in anderen Antworten beschrieben sind, sind wahrscheinlich die beste Wahl.

Wenn Sie verrückt genug sind, Perl-Skripte als Root auszuführen, können Sie dies mit den $< $( $> $) Variablen tun , die echte / effektive UID / GID enthalten, z.

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
P-Muttern
quelle
-1 , wie es IS mit sudo möglich , andere Benutzer-Rechte vorübergehend zu gewinnen.
Kimvais
4
Es ist nicht in dem Sinne möglich, dass der Benutzer, den das Shell-Skript selbst ausführt, nicht geändert werden kann (was in der ursprünglichen Frage gestellt wurde). Das Aufrufen anderer Prozesse mit sudo ändert nichts daran, als wen das Skript selbst ausgeführt wird.
P-Nuts
2

Das hat bei mir funktioniert

Ich habe meine "Bereitstellung" von meinem "Start" getrennt.

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

dann in meiner 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
quelle
-2

Inspiriert von der Idee von @ MarSoft, aber ich habe die Zeilen wie folgt geändert:

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

Ich habe verwendet sudo, um ein Passwort weniger Ausführung des Skripts zu ermöglichen. Wenn Sie ein Kennwort für den Benutzer eingeben möchten, entfernen Sie das sudo. Wenn Sie die Umgebungsvariablen nicht benötigen, entfernen Sie sie -Eaus sudo.

Die /usr/bin/bash -lstellt sicher, dass die profile.dSkripte für eine initialisierte Umgebung ausgeführt werden.

Trendfischer
quelle
sudomag praktisch sein, aber es ist nicht gut, wenn es nicht sofort einsatzbereit ist (wie unter AIX). su -c 'commands'ist die richtige Antwort.
Tricky
@Tricky Vielleicht lesen Sie die vollständige Antwort, es schlägt bereits vor, zu entfernen sudo. Tatsächlich ist sudo nicht so einfach. In vielen Fällen benötigen Sie sogar sudo -Eeinen Konfigurationseintrag in sudoers.d, um die Ausführung ohne tty mit zu ermöglichen !requiretty. Es gibt jedoch viele Fälle, in denen sudo für automatisch aufgerufene Skripte erforderlich ist, in denen ein Kennwortdialog stören kann. Daher würde ich es nicht aus einer Standardlösung entfernen.
Trendfischer
Der fragliche Code ist geradezu fehlerhaft - er bricht Skriptnamen oder Argumente mit Leerzeichen; Beachten Sie, dass das "$@"Einfügen in eine Zeichenfolge bedeutet, dass Argumente nach der ersten Zeichenfolge an eine separate Zeichenfolge angehängt werden, die nicht im -cArgument enthalten ist. Wenn Sie es sicher machen wollten, könnten Sie printf -v arg_q '%q ' "$0" "$@"und dannsu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy
@ CharlesDuffy Du hast recht! Ich habe mein Produktionsskript für diese Antwort zu stark vereinfacht :-( Nur das Hinzufügen eines Skripts COMMANDARGS=$@löst das Problem mit -c. Argumente mit Leerzeichen waren vorher kein Problem, aber ich habe Ihre gute Eingabe implementiert. Ich musste nur einige Experimente durchführen, damit es funktioniert Ich habe die Frage bearbeitet und hoffentlich habe ich keinen weiteren Fehler
eingefügt