使用Python以特定权限运行Shell脚本

15

我有一个名为update.sh的最简单脚本。

#!/bin/sh
cd /home/pi/circulation_of_circuits
git pull

在终端中使用./update.sh调用时,我会得到一个已经是最新的的提示或者按预期更新文件。

我还有一个Python脚本,里面包含:

subprocess.call(['./update.sh'])

当那个脚本调用相同的脚本时,我会得到以下错误提示:

权限被拒绝(publickey)。 fatal: Could not read from remote repository。

请确保您拥有正确的访问权限和存储库存在。

(我使用SSH)。

----------------- 更新 --------------------

有人替我看了看:

好的,有些进展。当我启动你的镜像时,我无法在你的仓库目录下运行git pull命令,bash脚本也失败了。似乎是因为bitbucket存储库是私有的,需要进行身份验证才能pull(我使用的是公共存储库,所以我没有问题)。显然,git在您第一次输入后就会记住这一点,bash以某种方式欺骗git,使其认为是您随后输入该命令,但从python运行它并不相同。

我不是git专家,但肯定有一些方法可以设置这个,使得python可以提供身份验证。


不,如果这很重要的话,我是这样调用它的cmd = ['/home/pi/circulation_of_circuits/update.sh'] process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) process.wait() - clankill3r
如果有人想知道,os.geteuid() 返回 0,所以应该没问题。 - clankill3r
1
“Permission denied (publickey)” 表示您的 SSH 配置存在问题。您是否尝试使用不同的用户运行 Python 脚本? - zzn
我也试过这样,例如 subprocess.call(['sudo','./update.sh']) 但是这并没有改变任何事情。 - clankill3r
1
也许你可以尝试像这样 cmd=['sudo', '-u', 'yourusername', 'path to your bash', '/home/pi/circulation_of_circuits/update.sh'],看看是否有所帮助?这将使用您自己的用户权限运行脚本,而不是sudo的权限。正如kennytm的评论所述,您不应该以root身份运行以执行git pull。 - Montmons
显示剩余13条评论
9个回答

11

听起来你需要给你的ssh命令提供一个它可以访问的公钥或私钥:

ssh -i /backup/home/user/.ssh/id_dsa user@unixserver1.nixcraft.com

-i参数用于指定在哪里查找密钥。


6
这个问题是由于git仓库认证失败引起的。你说你正在使用SSH,而git则抱怨公钥身份验证失败。通常情况下,您可以在私人仓库上使用git命令而无需输入密码。所有这些都意味着git正在使用ssh,但在后一种情况下,它找不到正确的私钥。
由于该问题只在通过另一个脚本运行时出现,很可能是由于某些东西干扰了环境变量。Subprocess.call应该按原样传递环境,因此有几个通常的嫌疑对象:
1. sudo。
如果您正在使用sudo,则会将几乎空的环境传递给进程
2. python脚本本身
如果python脚本更改其环境,则这些更改也将传播到子进程中。
3. sh -l或su -
这些命令设置登录Shell,这意味着它们的环境会重置为默认值。
任何这些原因都可能隐藏ssh-agent(或其他密钥管理工具)需要工作的环境变量。
诊断和修复步骤:
1. 隔离问题。
创建一个最小的Python脚本,只运行subprocess.call(['./update.sh'])。运行update.sh和新脚本。
2. 诊断问题并相应地进行修复:
a) 如果update.sh有效,而新脚本无效,则您可能正在经历一些怪异的系统配置角落情况。尝试升级您的系统和Python;如果问题仍然存在,则可能需要在受影响的系统上进行额外的调试。
b) 如果update.sh和新脚本都有效,则问题出现在调用shell脚本的外部python脚本中。搜索sudo,su-,sh-l,env和os.environ出现的位置,其中一个最有可能是罪魁祸首。
c) 如果既不更新.sh也不新脚本有效,则您的问题可能与ssh客户端配置有关。典型原因可能是您正在使用非默认身份验证身份,没有在~/.ssh/config中配置它,而是使用了ssh-add,之后ssh-agent的缓存过期了。在这种情况下,对于用于身份验证到那个git仓库的身份,运行ssh-add identityfile,然后重试。

3

在执行ssh-add之前,您可能还需要使用以下命令启动ssh-agent:eval `ssh-agent -s` - Aneesh Panoli

3

看起来你的版本控制系统需要对 pull 进行身份验证,这样才能使用 pexpect 构建 Python 代码。

import pexpect
child = pexpect.spawn('./update.sh')
child.expect('Password:')
child.sendline('SuperSecretPassword')

2

尝试使用sh包而不是使用子进程调用。 https://pypi.python.org/pypi/sh我尝试了这个代码片段,它对我有效。

#!/usr/local/bin/python

import sh

sh.cd("/Users/siyer/workspace/scripts")
print sh.git("pull")

输出:

已经是最新版本。


2
import subprocess 

subprocess.call("sh update.sh", shell=True)

1
虽然这段代码可能回答了问题,但是提供关于为什么和/或如何回答问题的额外上下文可以提高其长期价值。 - Donald Duck
我已经添加了 "sh update.sh",因为我们可以用两种方式执行shell脚本,即1)./update.sh和2)sh update.sh,在这里两者都应该起作用。但是对于他来说,第一种方法有效,所以我建议他尝试第二种方法。谢谢。 - Kanagaraj Dhanapal
你可以编辑你的回答以包含更多信息。 - Donald Duck

1
我可以重现你的故障。这与权限无关,而是取决于你的系统上安装了如何的ssh。为了验证是否是相同的原因,我需要diff输出。请将以下内容保存到文件log_shell_env.sh中。
#!/bin/bash

log="shell_env"$1
echo "create shell_env"$1

echo "shell_env" > $log

echo "whoami="$(whoami) >> $log
echo "which git="$(which git) >> $log
echo "git status="$(git status 2>&1) >> $log
echo "git pull="$(git pull 2>&1) >> $log
echo "ssh -vT git@github.com="$(ssh -T git@github.com 2>&1) >> $log

echo "ssh -V="$(ssh -V 2>&1) >> $log
echo "ls -al ~/.ssh="$(ls -a ~/.ssh) >> $log

echo "which ssh-askpass="$(which ssh-askpass) >> $log
echo "ps -e | grep [s]sh-agent="$(ps -e | grep [s]sh-agent ) >> $log
echo "ssh-add -l="$(ssh-add -l) >> $log

echo "set=" >> $log
set  >> $log

设置执行权限并运行它两次:
1. 从控制台中不带参数运行
2. 从你的Python脚本中带参数'.python'运行
请确保真正从相同的Python脚本中运行!

   For instance:
    try:
        output= subprocess.check_output(['./log_shell_env.sh', '.python'], stderr=subprocess.STDOUT)
        print(output.decode('utf-8'))

    except subprocess.CalledProcessError as cpe:
        print('[ERROR] check_output: %s' % cpe)

请执行diff shell_env shell_env.python > shell_env.diff命令。 生成的shell_env.diff文件应该只显示以下差异:

15,16c15,16  
< BASH_ARGC=()
< BASH_ARGV=()
---
> BASH_ARGC=([0]="1")
> BASH_ARGV=([0]=".python")
48c48
< PPID=2209
---
> PPID=2220
72c72
< log=shell_env
---
> log=shell_env.python

如果你得到了更多的差异,请回来并发表评论,在问题中更新差异输出。

1

如果您使用 Git 1.7.9 或更高版本,可以使用以下凭据助手之一:

带有超时功能

git config --global credential.helper cache

...这告诉Git在内存中缓存您的密码,默认情况下为15分钟。您可以使用以下命令设置更长的超时时间:

git config --global credential.helper "cache --timeout=3600"

这个例子是在GitHub帮助页面中针对Linux提出的。如果需要,您还可以永久存储您的凭据。

永久保存

您可以通过git-credential-store进行操作。

git config credential.helper store

GitHub的帮助还建议,如果您使用Homebrew在Mac OS X上安装Git,则可以使用本机Mac OS X密钥库:

git config --global credential.helper osxkeychain

对于Windows系统,有一个叫做Git Credential Manager for Windows或msysgit中的wincred助手。
git config --global credential.helper wincred # obsolete

使用Git for Windows 2.7.3+(2016年3月):
git config --global credential.helper manager

对于Linux,您可以使用gnome-keyring(或其他密钥环实现,如KWallet)。
最后,在手动执行建议的命令一次之后,您可以在不更改脚本的情况下执行您的脚本。

@clankill3r 你试过我建议的吗?如果您的问题得到很好的记录,对我们所有人都有好处。 - sdikby

1
使用以下Python代码。这将在Python中导入os模块并使用sudo权限进行系统调用。
#!/bin/python
import os 
os.system("sudo ./update.sh")

如果他们想使用一个非root用户怎么办?或者他们想在不输入密码的情况下执行此操作? - tahsmith
如果用户不是root,则他们至少必须是sudo组的成员。Shell脚本还必须对sudo组具有可执行权限,否则sudo bash update.sh就足够了。有一些方法可以在不输入密码的情况下完成此操作,但它们很危险,我建议不要这样做。 - Alexander Collins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接