使用Python关机Linux电脑

19 投票
10 回答
53158 浏览
提问于 2025-04-18 02:21

我正在尝试写一个脚本,如果满足一些条件,就让电脑关机。

os.system("poweroff")

我还试过

os.system("shutdown now -h")

还有其他一些方法。但是当我运行这些代码时,电脑只是按照代码执行,没有崩溃,也没有出现任何错误信息,最后正常结束了脚本,但电脑并没有关机。

请问怎么用Python让电脑关机呢?

补充说明:看起来我尝试的命令需要管理员权限。有办法在不提升权限的情况下从脚本中关机吗?

10 个回答

3

我只是想补充一下@BorrajaX的回答,特别是对于使用logind的用户(比如较新的Fedora系统):

import dbus
sys_bus = dbus.SystemBus()
lg = sys_bus.get_object('org.freedesktop.login1','/org/freedesktop/login1')
pwr_mgmt =  dbus.Interface(lg,'org.freedesktop.login1.Manager')
shutdown_method = pwr_mgmt.get_dbus_method("PowerOff")
shutdown_method(True)
3

有没有办法在没有管理员权限的情况下关机呢?

没有这个办法(幸好没有!)。

不过要记住,有一些系统功能可以让普通用户更容易获得提升权限的机会:

  • sudo:这个命令可以让普通用户以管理员的身份执行某些命令。
  • setuid:这是一个文件权限设置,可以让用户在运行某个程序时临时获得更高的权限。
  • setcap:这个命令可以给程序设置特定的权限,让它在不需要提升用户权限的情况下执行某些操作。
9

关闭系统的最佳方法是使用以下代码

import os
os.system('systemctl poweroff') 
27
import os
os.system("shutdown now -h")

用管理员权限来运行你的脚本。

22

很多Linux系统在执行shutdownhalt命令时需要超级用户权限,但如果你坐在电脑前,为什么可以直接关机而不需要成为root呢?你只需打开菜单,点击关机,电脑就关掉了,对吧?

其实,这背后的原因是,如果你能物理接触到电脑,比如直接拔掉电源线,那你就可以随意关机。因此,现在很多Linux系统允许通过本地系统总线来关机,这个总线可以通过dbus访问。不过,dbus(或者说通过它提供的服务)是不断变化的。我建议你安装一个dbus查看工具,比如D-feet(不过要注意:它的可视化还是有点难,但可能会有帮助)。

你可以看看这些Dbus关机脚本

如果你的系统中还有HAL(这个正在被淘汰),可以试试这个:

import dbus
sys_bus = dbus.SystemBus()
hal_srvc = sys_bus.get_object('org.freedesktop.Hal',
                              '/org/freedesktop/Hal/devices/computer')
pwr_mgmt =  dbus.Interface(hal_srvc,
                'org.freedesktop.Hal.Device.SystemPowerManagement')
shutdown_method = pwr_mgmt.get_dbus_method("Shutdown")
shutdown_method()

这个方法在Ubuntu 12.04上是有效的(我刚刚关机确认过)。如果你用的是更新的版本……那可能就不行了。这就是这个方法的缺点:它非常依赖于具体的发行版。

你可能需要安装dbus-python包才能让它工作(http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html)。

更新 1:

我做了一些研究,发现新版本的Ubuntu是通过ConsoleKit来实现的。我在我的Ubuntu 12.04(同时有被淘汰的HAL和更新的ConsoleKit)上测试了下面的代码,它确实关掉了我的电脑:

>>> import dbus
>>> sys_bus = dbus.SystemBus()
>>> ck_srv = sys_bus.get_object('org.freedesktop.ConsoleKit',
                                '/org/freedesktop/ConsoleKit/Manager')
>>> ck_iface = dbus.Interface(ck_srv, 'org.freedesktop.ConsoleKit.Manager')
>>> stop_method = ck_iface.get_dbus_method("Stop")
>>> stop_method()

更新 2:

为什么你可以在不成为root的情况下做到这一点,其实值得更详细的解释。我们先关注新的ConsoleKit(因为HAL要复杂得多,我个人认为)。

ConsoleKit是一个以root身份在系统中运行的服务:

borrajax@borrajax:/tmp$ ps aux|grep console-kit
root 1590  0.0  0.0 1043056 3876 ? Sl   Dec05   0:00 /usr/sbin/console-kit-daemon --no-daemon

现在,d-bus只是一个消息传递系统。你有一个服务,比如ConsoleKit,它向d-bus提供一个接口。它暴露的一个方法是Stop(如上所示)。ConsoleKit的权限是通过PolKit来控制的,尽管它是基于常规Linux权限,但提供了更细致的控制,比如“谁可以做什么”。例如,PolKit可以规定:“如果用户已经登录到电脑上,就允许他做某事;如果是远程连接,就不允许。”如果PolKit判断你的用户可以调用ConsoleKitStop方法,那么这个请求就会通过d-bus传递给ConsoleKit(它随后会关掉你的电脑,因为它有这个权限……因为它是root)。

进一步阅读:

总结一下:你不能在不成为root的情况下关掉电脑,但你可以告诉一个以root身份运行的服务来为你关机。

更新 3:

在2021年12月,原答案发布七年后,我又需要做这个。这次是在Ubuntu 18.04上。

不出所料,情况似乎有些变化:

所以我们仍然可以用一个没有特权的脚本来关掉机器:

#! /usr/bin/python3
from pydbus import SystemBus
bus = SystemBus()

proxy = bus.get('org.freedesktop.login1', '/org/freedesktop/login1')
if proxy.CanPowerOff() == 'yes':
    proxy.PowerOff(False)  # False for 'NOT interactive'

更新 3.1:

看起来这并没有我想象的那么“新”X-D

在这个线程中,@Roeften已经给出了一个答案

额外信息:

我在你们的评论中看到你想在耗时的任务后关掉电脑,以防止过热……你知道吗,你可能可以在特定时间用RTC重新开机?(见这个这个)挺酷的,对吧?(我发现我可以这样做时兴奋得不得了……):-D

撰写回答