通过 os.system 推送

Posted

技术标签:

【中文标题】通过 os.system 推送【英文标题】:pushd through os.system 【发布时间】:2011-09-05 20:59:13 【问题描述】:

我正在使用 crontab 为我的 minecraft 服务器运行维护脚本。大多数情况下它工作正常,除非 crontab 尝试使用重启脚本。如果我手动运行重新启动脚本,则没有任何问题。因为我相信它与路径名有关,所以我试图确保它总是从 minecraft 目录执行任何 minecraft 命令。所以我将命令封装在 pushd/popd 中:

os.system("pushd /directory/path/here")
os.system("command to sent to minecraft")
os.system("popd")

下面是一个交互式会话,将我的世界排除在等式之外。一个简单的“ls”测试。如您所见,它根本没有从 pushd 目录运行 os.system 命令,而是从 /etc/ 运行 python 来说明我的观点的目录。显然 pushd 不能通过 python 工作,所以我想知道我还能如何实现这一目标。谢谢!

>>> def test():
...     import os
...     os.system("pushd /home/[path_goes_here]/minecraft")
...     os.system("ls")
...     os.system("popd")
... 
>>> test()
~/minecraft /etc
DIR_COLORS    cron.weekly  gcrypt         inputrc    localtime   mime.types         ntp       ppp         rc3.d       sasldb2         smrsh      vsftpd.ftpusers
DIR_COLORS.xterm  crontab      gpm-root.conf      iproute2   login.defs  mke2fs.conf            ntp.conf      printcap        rc4.d       screenrc        snmp       vsftpd.tpsave
X11       csh.cshrc    group          issue      logrotate.conf  modprobe.d         odbc.ini      profile         rc5.d       scsi_id.config  squirrelmail   vz
adjtime       csh.login    group-         issue.net  logrotate.d     motd               odbcinst.ini  profile.d       rc6.d       securetty       ssh        warnquota.conf
aliases       cyrus.conf   host.conf      java       lvm         mtab               openldap      protocols       redhat-release  security        stunnel        webalizer.conf
alsa          dbus-1       hosts          jvm        lynx-site.cfg   multipath.conf         opt       quotagrpadmins  resolv.conf     selinux         sudoers        wgetrc
alternatives      default      hosts.allow    jvm-commmon    lynx.cfg    my.cnf             pam.d         quotatab        rndc.key        sensors.conf    sysconfig      xinetd.conf
bashrc        depmod.d     hosts.deny     jwhois.conf    mail        named.caching-nameserver.conf  passwd        rc          rpc         services        sysctl.conf    xinetd.d
blkid         dev.d        httpd          krb5.conf  mail.rc     named.conf         passwd-       rc.d        rpm         sestatus.conf   termcap        yum
cron.d        environment  imapd.conf     ld.so.cache    mailcap     named.rfc1912.zones        pear.conf     rc.local        rsyslog.conf    setuptool.d     udev       yum.conf
cron.daily    exports      imapd.conf.tpsave  ld.so.conf     mailman     netplug            php.d         rc.sysinit      rwtab       shadow          updatedb.conf  yum.repos.d
cron.deny     filesystems  init.d         ld.so.conf.d   makedev.d   netplug.d          php.ini       rc0.d       rwtab.d         shadow-         vimrc
cron.hourly   fonts        initlog.conf   libaudit.conf  man.config  nscd.conf          pki       rc1.d       samba       shells          virc
cron.monthly      fstab        inittab        libuser.conf   maven       nsswitch.conf          postfix       rc2.d       sasl2       skel        vsftpd
sh: line 0: popd: directory stack empty

=== (CentOS 服务器与 python 2.4)

【问题讨论】:

我对“~/minecraft /etc”这行有点困惑。在我看来,os.system 生成子shell 的事实看起来很简单……执行bash -c "pushd directory"bash -c "popd" 会给你同样的结果……为什么不直接使用os.chdir nvm 关于混淆线,这是您正在执行的pushd 的输出,但分析仍然有效,您的命令不起作用,因为os.system 产生了一个子shell。 ...一旦子shell完成,pushd/popd上下文就变得毫无意义。 【参考方案1】:

在 Python 2.5 及更高版本中,我认为更好的方法是使用上下文管理器,如下所示:

import contextlib
import os


@contextlib.contextmanager
def pushd(new_dir):
    previous_dir = os.getcwd()
    os.chdir(new_dir)
    try:
        yield
    finally:
        os.chdir(previous_dir)

然后你可以像下面这样使用它:

with pushd('somewhere'):
    print os.getcwd() # "somewhere"

print os.getcwd() # "wherever you started"

通过使用上下文管理器,您将获得异常和返回值安全:您的代码将始终 cd 回到它开始的位置,即使您抛出异常或从上下文块内部返回。

您还可以将 pushd 调用嵌套在嵌套块中,而不必依赖全局目录堆栈:

with pushd('somewhere'):
    # do something
    with pushd('another/place'):
        # do something else
    # do something back in "somewhere"

【讨论】:

我喜欢这个想法,它更加优雅和 Python :) 这就是我一直在寻找的。但是,如果在 with 语句中引发异常,则此代码不会弹出,因为 @contextmanager 不处理异常 docs.python.org/2/library/contextlib.html 您需要用 try 包围 yield ...finally @MaximeViargues 还有 contextlib closing 方法,它负责 try...finally 对你的要求docs.python.org/2/library/contextlib.html#contextlib.closing 只有名字选得不好,恕我直言;因为你没有像 sh 脚本中那样的 push/pop 对称,因为 popd 是隐式的【参考方案2】:

每个 shell 命令都在单独的进程中运行。它生成一个 shell,执行 pushd 命令,然后 shell 退出。

只需在同一个 shell 脚本中编写命令:

os.system("cd /directory/path/here; run the commands")

一个更好的(也许)方法是使用subprocess 模块:

from subprocess import Popen
Popen("run the commands", shell=True, cwd="/directory/path/here")

【讨论】:

我刚试过第二种方法;我最近才开始使用子进程(这里是新程序员。显然。)我想更好地学习。它可以工作,但是我必须按 CTRL+C 才能恢复 python 提示。奇数。 值得注意的是,在带有 UNC 路径作为参数的 Windows 机器上使用 pushd 会导致 Windows 自动将网络驱动器映射到路径。使用 cd 不适用于 UNC 路径,Popen 也不起作用。如果需要该功能,可以使用Popen(r'pushd \\server\folder & dir & popd', shell=True)。另请注意,; 不会在 Windows 中分隔命令,但 & 会。 我发现映射 Windows 驱动器可能需要 30 秒。你真的要等那么久才能运行你的 python 脚本吗?【参考方案3】:

pushdpopd 具有一些附加功能:它们将以前的工作目录存储在堆栈中 - 换句话说,您可以 pushd 五次,做一些事情,然后 popd 五次以结束你开始了。您在这里没有使用它,但它可能对其他搜索此类问题的人有用。这就是你可以模仿它的方式:

# initialise a directory stack
pushstack = list()

def pushdir(dirname):
  global pushstack
  pushstack.append(os.getcwd())
  os.chdir(dirname)

def popdir():
  global pushstack
  os.chdir(pushstack.pop())

【讨论】:

【参考方案4】:

我认为您不能在 os.system() 通话中拨打 pushd

>>> import os
>>> ret = os.system("pushd /tmp")
sh: pushd: not found

也许只是你的系统实际上提供了一个pushd 二进制文件来触发一个 shell 内部函数(我想我以前在 FreeBSD 上见过这个FreeBSD has some tricks like this, but not for pushd),但是当前进程的工作目录 不会受到其他进程的影响——所以你的第一个system() 启动一个shell,运行一个假设的pushd,启动一个shell,运行ls,启动一个shell,运行一个假设popd...它们都不会相互影响。

可以使用os.chdir("/home/path/")更改路径:http://docs.python.org/library/os.html#os-file-dir

【讨论】:

【参考方案5】:

无需使用pushd -- 只需使用os.chdir

>>> import os
>>> os.getcwd()
'/Users/me'
>>> os.chdir('..')
>>> os.getcwd()
'/Users'
>>> os.chdir('me')
>>> os.getcwd()
'/Users/me'

【讨论】:

是的。这正是我想要的。谢谢! 这仅在前一个目录是另一个目录的直接父目录时才有效。 @DaveKennedy,是的。我想你也可以说接受的答案只有在目录是'/directory/path/here' 时才有效,不是吗?【参考方案6】:

或者创建一个与'with'一起使用的类

import os

class pushd: # pylint: disable=invalid-name
    __slots__ = ('_pushstack',)

    def __init__(self, dirname):
        self._pushstack = list()
        self.pushd(dirname)

    def __enter__(self):
        return self

    def __exit__(self, exec_type, exec_val, exc_tb) -> bool:
        # skip all the intermediate directories, just go back to the original one.
        if self._pushstack:
            os.chdir(self._pushstack.pop(0)))
        if exec_type:
            return False
        return True

    def popd(self) -> None:
        if len(self._pushstack):
            os.chdir(self._pushstack.pop())

    def pushd(self, dirname) -> None:
        self._pushstack.append(os.getcwd())
        os.chdir(dirname)


    with pushd(dirname) as d:
        ... do stuff in that dirname
        d.pushd("../..")
        d.popd()

【讨论】:

【参考方案7】:

如果你真的需要一个堆栈,即如果你想做几个 pushd 和 popd, 参见上面的 naught101。

如果没有,只需这样做:

olddir = os.getcwd()
os.chdir('/directory/path/here')
os.system("command to sent to minecraft")
os.chdir(olddir)

【讨论】:

以上是关于通过 os.system 推送的主要内容,如果未能解决你的问题,请参考以下文章

如何确定通过 os.system 启动的进程的 pid

(Python/Linux) 通过 python 或 os.system 命令播放无延迟的 wav 文件

Python执行系统命令的方法 os.system(),os.popen(),commands

使用代理将 DataFlow 作业连接到 Cloud MySQL 是不是安全(通过 os.system)

Python:如何保存 os.system 的输出 [重复]

克服 Python 2.3 中的 os.system() 限制