Как использовать su для выполнения остальной части сценария bash от имени этого пользователя?

126

Я написал сценарий, который принимает в качестве аргумента строку, которая представляет собой объединение имени пользователя и проекта. Предполагается, что сценарий переключит (su) на имя пользователя, cd в конкретный каталог на основе строки проекта.

Я в основном хочу сделать:

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

Проблема в том, что как только я делаю су ... он просто ждет там. Это имеет смысл, поскольку поток выполнения перешел к переключению на пользователя. Как только я выхожу, все остальное выполняется, но не работает должным образом.

Я добавил su к команде svn, но команда не удалась (т.е. она не обновила svn в желаемом каталоге).

Как мне написать сценарий, который позволяет пользователю переключать пользователя и вызывать svn (среди прочего)?

Avery
источник

Ответы:

86

Уловка состоит в том, чтобы использовать команду «sudo» вместо «su»

Вам может потребоваться добавить это

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

в ваш файл / etc / sudoers

и измените свой сценарий на:

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

Где username2 - это пользователь, от имени которого вы хотите запустить команду SVN, а username1 - это пользователь, запускающий сценарий.

Если вам нужно несколько пользователей для запуска этого сценария, используйте %groupnameвместо имени пользователя1

Kimvais
источник
У меня похожая проблема, но я хотел запустить ее chshдля других пользователей. Моя проблема указана здесь, на stackoverflow.com/q/15307289/80353 Как мне адаптировать ваш ответ к моей ситуации?
Ким Стэкс
Я сделал это - но он все равно запросил пароль.
Hippyjim
@Hippyjim, вы уверены, что правильно выбрали имена пользователей?
Kimvais
1
Я сделал - оказалось, что мне также нужно было разрешить использование / bin / bash.
Hippyjim
3
Независимо от того, используете ли вы sudoили suимеет второстепенное значение, sudoэто намного безопаснее и удобнее.
Tripleee
105

Гораздо проще: использовать sudoдля запуска оболочки и использовать heredoc для подачи ей команд.

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

(ответ изначально на SuperUser )

Дэн Даскалеску
источник
5
Этот ответ сработал лучше всего. Я рекомендую использовать опцию -iдля получения someuserожидаемого окружения.
not2savvy
2
sudoможет быть удобно, но не годится, если он не из коробки (как в AIX). su -c 'commands'это правильный ответ.
Tricky
1
Эпическое решение!
3bdalla
Как сделать переменные доступными в рамках Herdoc?
dotslashlu
в AIX эта ошибка выдается ksh: sh: 0403-006 Отказано в разрешении на выполнение.
AhmedRana
54

Используйте сценарий, подобный следующему, для выполнения остальной части сценария или его части под другим пользователем:

#!/bin/sh

id

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

id

eof
Walder
источник
11
Вы можете использовать «sudo -i -u ...», чтобы убедиться, что такие вещи, как $ HOME, установлены правильно.
Брайан Ларсен
Извините за вопрос нуба, но что здесь за идентификатор?
Нитин Джадхав
1
@NitinJadhav, он использовал его здесь, чтобы показать идентификатор текущего пользователя, идентификатор root равен 0, поэтому первый идентификатор покажет вам какое-то число, но второй определенно покажет 0 (потому что второй был выполнен внутри блок, запускаемый root). Вы можете user, whoamiвместо idкоторого будет возвращать имя вместо id
Мохаммед Нурелдин
@MohammedNoureldin Спасибо!
Нитин Джадхав
sudoможет быть удобно, но не годится, если он не из коробки (как в AIX). su -c 'commands'это правильный ответ.
Tricky
52

Вам необходимо выполнить все команды для разных пользователей как отдельный сценарий. Если это всего одна или несколько команд, то встроенный должен работать. Если команд много, то, вероятно, лучше переместить их в отдельный файл.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Дуглас Лидер
источник
4
Это единственно правильный ответ. Для этого sudo не требуется.
helvete
1
Вам также может потребоваться предоставить оболочку su -s /bin/bash.
mixel
лучшее решение для root-пользователей
rfinz
Лучший ответ для тех, у кого sudo не установлен по умолчанию (я смотрю на вас, AIX)
Tricky
46

Вот еще один подход, который в моем случае был более удобен (я просто хотел отказаться от привилегий root и выполнить остальную часть сценария от ограниченного пользователя): вы можете перезапустить сценарий от правильного пользователя. Предположим, что он изначально запущен как root. Тогда это будет выглядеть так:

#!/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
источник
6
Интересно, почему это не выше рейтинга. Он лучше всего решает исходный вопрос, сохраняя при этом все в bash.
SirVer 04
Или runuser -u $user -- "$@", как указано в su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
macieksk
1
Спасибо @macieksk, хороший улов. Буду обновлять. Это --действительно полезно.
MarSoft
1
Представленный подход лучший из всех и закончен. Все написано внутри одного скрипта, и все используют bash. Фактически, этот сценарий запускается дважды. Сначала тестируем скрипт, это root, затем готовим окружение и меняем пользователя с помощью su. Но сделайте это с помощью команды exec, тогда будет только один экземпляр скрипта. Во втором цикле фраза if / fi опущена, тогда сценарий непосредственно выполняет подготовленное действие. В этом примере это эхо. Все остальные подходы решают проблему лишь частично. Конечно, этот скрипт можно запустить под sh, а не под bash, но мы должны тестировать специальную переменную $ HOME, а не $ UID
Зник
7

Используйте sudoвместо

EDIT : Как Дуглас отметил, вы не можете использовать cdв , sudoтак как он не является внешней командой. Вы должны запускать команды в подоболочке, чтобы заставить cdработать.

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

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

Вас могут попросить ввести пароль этого пользователя, но только один раз.

iamamac
источник
Однако это не сработает - компакт-диск будет потерян после завершения выполнения первого sudo.
Дуглас Лидер 01
Фактически, вы даже не можете вызвать cd напрямую, потому что это не внешняя команда.
iamamac 01
sudoможет быть удобно, но не годится, если он не из коробки (как в AIX). su -c 'commands'это правильный ответ.
Tricky
6

Сменить пользователя в сценарии оболочки невозможно. Обходные пути с использованием sudo, описанные в других ответах, вероятно, будут вашим лучшим выбором.

Если вы достаточно безумны, чтобы запускать сценарии Perl от имени пользователя root, вы можете сделать это с помощью $< $( $> $) переменных, которые содержат реальные / эффективные uid / gid, например:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
P-гайка
источник
-1 , как это IS можно с Судом временно получить права других пользователей.
Kimvais 01
4
Это невозможно в том смысле, что пользователь, запускающий сам сценарий оболочки, не может быть изменен (что и было задано в исходном вопросе). Вызов других процессов с помощью sudo не меняет того, от кого запускается сам сценарий.
P-Nuts
2

Это сработало для меня

Я отделил свою «подготовку» от «стартапа».

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

затем в моем 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
источник
-2

Вдохновленный идеей @ MarSoft, я изменил строки следующим образом:

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

Я использовал, sudoчтобы разрешить выполнение сценария без пароля. Если вы хотите ввести пароль для пользователя, удалите расширение sudo. Если вам не нужны переменные среды, удалите их -Eиз sudo.

Это /usr/bin/bash -lобеспечивает выполнение profile.dсценариев для инициализированной среды.

Trendfischer
источник
sudoможет быть удобно, но не годится, если он не из коробки (как в AIX). su -c 'commands'это правильный ответ.
Tricky,
@Tricky Возможно, прочтите полный ответ, он уже предлагает удалить sudo. На самом деле, sudo не так прост, во многих случаях вам даже нужна sudo -Eзапись конфигурации в sudoers.d, чтобы разрешить выполнение без tty с !requiretty. Но есть много случаев, когда sudo необходимо для автоматически вызываемых скриптов, когда диалог пароля может мешать. Таким образом, я бы не стал убирать его из стандартного решения.
Trendfischer
Рассматриваемый код откровенно ошибочен - он разбивает имена скриптов или аргументы пробелами; имейте в виду, что вставка "$@"внутри строки означает, что аргументы после первого добавляются в отдельную строку, а не включаются в -cаргумент. Если вы хотите сделать это безопасным, вы могли бы printf -v arg_q '%q ' "$0" "$@"и затем использоватьsu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Чарльз Даффи
@CharlesDuffy Вы правы! Я слишком упростил свой производственный сценарий для этого ответа :-( Простое добавление COMMANDARGS=$@решает проблему с -c. Аргументы с пробелами не были проблемой раньше, но я реализовал ваш хороший ввод. Мне просто пришлось провести несколько экспериментов, чтобы заставить его работать. Я отредактировал вопрос и, надеюсь, я не вставил еще одну ошибку. Спасибо за ваш комментарий, унизительный, но необходимый.
Trendfischer