bash选项”-i”在从Python调用时实际上做什么?

huangapple go评论80阅读模式
英文:

What does the bash option -i actually do when bash is invoked from Python?

问题

以下是您要翻译的部分:

"如果我在带有Python 3.11.2的Linux Mint 21 Cinnamon版本上运行以下Python脚本:"

import os
T = "gnome-terminal"
os.system(f'{T} -- python -ic"import os; os.system(\'resize -s 5 32\')\'')
os.system(f'{T} -- bash -ic "resize -s 5 32"')

它将创建以下两个终端窗口:

注意,脚本的两行都执行相同的任务并使用相同的命令行选项。使用Python的行不需要在“-ic”和字符串之间添加空格,而使用bash的行需要。在这种情况下有一个问题:

“我的期望有什么问题,即两行都应该提供一个交互式终端窗口?”第一行如预期地导致交互式Python会话,但第二行无法提供交互式会话,当终端设置中选项在子进程退出后保持打开的情况下,根本不会升起终端窗口。如果Python退出,该会话将退回到bash shell提示符,而不是退出终端窗口。"

下面是脚本的修改,以便在Python退出时返回到bash shell提示符:

import os
T = "gnome-terminal"
os.system(f'{T} -- python -ic "import os; os.system(\'resize -s 5 32\'); import code; code.interact(local=locals())"')
os.system(f'{T} -- bash -ic "resize -s 5 32"')

这些修改会在Python退出时返回到bash shell提示符。

英文:

If I run following Python script on Linux Mint 21 Cinnamon edition with Python 3.11.2:

import os
T = "gnome-terminal"
os.system(f'{T} -- python -ic"import os; os.system(\'resize -s 5 32\')"')
os.system(f'{T} -- bash   -ic "resize -s 5 32"')

it creates following two Terminal windows:

bash选项”-i”在从Python调用时实际上做什么?


UPDATE as reaction to a comment by Cyrus requesting to provide text instead of an image: if I replace the image with the text it would give in case of the first window:

COLUMNS=32;
LINES=5;
export COLUMNS LINES;
>>> 

and in the case of the second window:

The child process exited normally with status 0.

making it as good as not possible to directly see what the question is about. Images are sometimes the much better option.


Notice that both of the script lines are doing the same job and are using the same command line options. The line using Python does not require a space between -ic and the string. The line with bash does. In this context a question:

What is wrong with my expectation that both lines should give an interactive Terminal window?

The first line results as expected in an interactive Python session, but the second line fails to give an interactive session and wouldn't rise a Terminal window at all when in the Terminal settings the option to stay opened after the child process exits wouldn't be checked.

I would be glad if someone in course of giving an answer to my question could also explains why the line with bash needs the space where the Python line doesn't and how to tweak the code creating the interactive Python session so that it will fall back to a bash shell prompt instead of exiting the Terminal window if Python exits.

UPDATE as reaction to comment by Charles Duffy:

The info bash command gives as an explanation of the -i option for bash: -i If the -i option is present, the shell is interactive. and man bash gives the same explanation. Interactive when not running??? Has the word interactive another meaning? Which one?

After reading the details of the bash -i option as suggested in the comments it turns out that the word interactive is severe misused here and means the opposite of what I would expect from it. So if I want the shell to respond to the provided code I have to run the shell NON-interactive. OK. In this context it seems that my actual question should be:

> If your real question is ("how do I get bash to run some arbitrary code and then drop to an interactive shell?", then maybe ask that directly. – Charles Duffy

Below my attempt to use subprocess:

from subprocess import run
run([f'{T}', '--', 'python', '-ic', '"import os; os.system(\'resize -s 5 32\')"'])
run([f'{T}', ' -- bash ', '-i' , '-c', '"resize -s 5 32"'])

The code above does not resize the first window and gives following Error message on the second one:

# Failed to parse arguments: Unknown option -i

Corrected the code for the second window to:

run([f'{T}', '--', 'bash ' , '-i' , '-c', '"resize -s 5 32"'])

Now both windows are created without resize and the second one gives:

# Error: Failed to execute child process “bash ”: Failed to execve: No such file or directory

答案1

得分: 2

如在 https://stackoverflow.com/questions/4584736/bash-execute-command-but-continue-with-interactive-session 的重复问题中所描述,可以使用 --rcfile 传递 bash 代码以在启动时运行:

import tempfile, subprocess

with tempfile.NamedTemporaryFile(prefix='bash-rcfile') as tf:
  tf.write(b'resize -s 5 32\n')
  tf.flush()
  subprocess.run(['gnome-terminal', '--', 'bash', '--rcfile', tf.name, '-i'])
英文:

As described in the duplicate at https://stackoverflow.com/questions/4584736/bash-execute-command-but-continue-with-interactive-session, --rcfile can be used to pass bash code to run on startup:

import tempfile, subprocess

with tempfile.NamedTemporaryFile(prefix='bash-rcfile') as tf:
  tf.write(b'resize -s 5 32\n')
  tf.flush()
  subprocess.run(['gnome-terminal', '--', 'bash', '--rcfile', tf.name, '-i'])

答案2

得分: 0

在编程世界中,你需要确保你的代码完全按照可能难以理解的规则编写,你可以采用另一种方法来实现你的目标,只依赖于应用程序的用户界面GUI,而不是为编程而公开的API。

这样,你只需要启动终端,然后使用一个自动化工具,比如pyautogui,来启动Python,已经在运行的交互式终端上具有一个bash提示符。

这是一种非常简单的方法,对于任何系统环境中的任何shell都可以以相同的方式工作,因此你不需要应对软件在编程或命令行API中存在错误或未实现预期的内部标准的情况,只需提供一个良好运行的用户界面。

这种方法的一个明显优势是你可以在屏幕上看到路径到结果的执行过程。

尝试以下代码以获取所需的窗口大小和一个运行中的Python shell会话,如果完成,将会保留一个仍然打开的终端窗口和一个可以使用的shell:

import os
import pyautogui
import time
from subprocess import run
run(["gnome-terminal","--geometry=32x5"])
time.sleep(0.25)
pyautogui.write("python\n")

当然...这种方法可能会在面对某些条件时失败,比如用户多任务处理速度快或系统原因。

实际上,似乎在启动窗口和发送按键之间有0.25秒的时间跨度(在我的机器上0.1秒已经足够)足以使代码按预期工作。

如果不行...只需增加这个值...希望不会遇到奇怪的情况。并避免在十分之一秒内切换到另一个窗口 ;)。

英文:

In the world of programming where you need your code to have been written exactly according to the maybe hard to understand rules, you can use another approach to achieve your goal relying only on the user interface GUI of the applications instead of their API exposed for usage in programming.

This way you just start a Terminal and then use an automation tool like for example pyautogui to start Python in the already running interactive Terminal with a bash prompt.

It is a very simple to go path and will work the same way for any shell in any system environment, so you don't need to cope with cases where the software has bugs in the programming or command-line API or does not implement expected internal standards focusing only on providing a well functioning user interface.

An apparent advantage of this approach is that you can SEE yourself on the screen what is done on the path to the result.

Try the code below to obtain the required window size and a running Python shell session which if finished will end up with still opened Terminal window and a ready to use shell:

import os
import pyautogui
import time
from subprocess import run
run(["gnome-terminal","--geometry=32x5"])
time.sleep(0.25)
pyautogui.write("python\n")

Sure ... such approach CAN and will fail if exposed to some conditions like fast user multitasking or system based reasons.

Actual it seems that a 0.25 sec. time span (on my machine 0.1 is already enough) between starting a window and sending it keystrokes is sufficient to make the code work as expected.
And if not ... just increase this value ... hoping to not face weird circumstances. And avoiding switching to another windows within tenths of a second bash选项”-i”在从Python调用时实际上做什么? .

答案3

得分: 0

让我们总结一下在评论中提到的内容以及提供的答案,同时回答您所提出的主要问题,即“我的期望是,这两行代码都应该提供一个交互式终端窗口,这个期望有什么问题?”

您的期望存在两个问题。首先,您期望在终端启动的Python会话中,如果退出,终端会回到bash提示符。然而,在这种情况下,终端无法回到bash会话,因为当终端启动Python会话时,根本没有涉及到bash,终端只与Python直接通信。因此,当Python退出时,没有bash可以回到。

第二个问题是期望使用bash命令的-i选项,简单地解释为在~ $ man bash~ $ info bash中的如果存在-i选项,则shell是交互式的,会使带有此选项启动的bash保持交互而不退出。换句话说,经验上的混淆核心在于期望软件手册中的一个单词反映了它的直观和通常的口语含义。

在软件世界中,如果遇到困惑,可能需要查看用户手册中的详细信息,以了解所使用的单词实际含义。这在这里适用于bash shell的-i选项,其原因很可能是历史性的,可以简短地归结为,启动的bash只要有任务要完成,它就会执行任务然后退出。

要在给予一些初步命令时保持shell活动,需要使用其-i选项,同时将初步命令作为bash脚本的内容提供,文件名由长--rcfile选项指定,紧随其后的是短-i选项,因为反过来就行不通,可能是因为需要满足某些POSIX标准的需要。顺便说一句:在启动shell之前,您可以(尚未测试)设置一个BASH_ENV环境变量,而不使用--rcfile选项。

尽管上面的内容已经完全回答了您的问题,但让我们在这里提供一些附加信息,也为了完整起见,还包括来自Charles Duffy回答问题的Python代码以及评论中提供的一些进一步解释:

下面的代码可以让bash shell执行一些初步工作,然后在完成调整终端窗口的工作和/或启动Python会话后仍然保持在终端中活动。这段代码使用更现代的subprocess模块,而不是os.system()调用,后者(用Charles Duffy的话来说)“创建了/bin/sh的额外副本,需要在您的命令之上添加额外的转义层,而不是直接传递参数向量”。此代码还需要使用Python分发中可用的tempfile模块作为创建bash实际可以获取其初步命令的文件的解决方法:

import tempfile, subprocess

with tempfile.NamedTemporaryFile(prefix='bash-rcfile') as tf:
  tf.write(b'resize -s 5 32\npython\n')
  tf.flush()
  subprocess.run(['gnome-terminal', '--', 'bash', '--rcfile', tf.name, '-i'])

上面的代码创建一个终端窗口,其中运行着一个交互式的Python会话,这里的特殊编码功能在于,当Python会话退出时,终端窗口保持活动并提供一个bash shell提示符。

为了完整起见,这里还提供了关于您的代码中一行代码不带空格运行正常,另一行不带空格不运行正常的解释(在* Socowi*的评论中提供):

简短回答:每个程序都决定如何解释其选项。较长的回答:因为每个程序都自行决定如何处理其选项,所以-i在两个不同程序之间可能具有完全不同的含义。它们甚至不必接受像-i这样的选项,甚至可以决定使用像°:#§i__这样的东西。这也适用于(可选的)空格。完整的答案:python的-i和bash的-i选项的实际含义可以在~ $ man python~ $ man bash中找到。- Socowi

顺便提一下:在设置启动的终端窗口的几何形状时,您可以直接使用gnome-terminal支持的--geometry参数,同时不仅可以设置窗口的大小,还可以设置创建的终端窗口的位置:

from tempfile import NamedTemporaryFile as ntf
from subprocess import run
with ntf() as tf:
  tf.write(b'python\n') # shell要完成的工作的命令
  tf.flush()
  run(['gnome-terminal', '--geometry=32x5+200+200', '--', 'bash', '--rcfile', tf.name, '-i'])

注意,上面的代码中,在--geometry选项中指定的X,Y(即宽度和高度)应与resize -s中它们的顺序相反。

最后,问题*当从Python中调用bash时,bash选项-i实际上是做什么?*需要更多的文本来进行准确的解释。可以确定的是,单独的-i并不足以使bash交互。上面的代码提供了这个选项的用例,可以满足问题中的要求。这应该足够作为答案。

英文:

Let's summarize what was said in the comments and what was provided as answers and along with doing that actually answer your prime question phrased as What is wrong with my expectation that both lines should give an interactive Terminal window?

The problem with your expectations is twofold. The first aspect of your (failing facing the facts) expectation is to expect that in a Terminal started Python session will fall back to a bash prompt if exited. The Terminal can't fall back into the bash session in that case just because when the Terminal starts the Python session bash isn't involved at all and the Terminal communicates directly only with Python. So there is no bash here to fall back to when Python exits.

The second aspect is to expect that a -i option of the bash command short explained in ~ $ man bash and ~ $ info bash as If the -i option is present, the shell is interactive will make the bash started with this option not exiting but staying interactive. In other words the core of experienced confusion is to expect that a word in a software manual mirrors its intuitive and usual spoken language meaning.

In the world of software if running into confusion it may be necessary to look at the very details in the user manual to see what a used word actually means. This is here the case for the -i option of the bash shell and the reason for this is most probably of historical nature and can be short nailed down to the fact that a started bash when given something to do just does the job and exits.

To keep the shell alive when given some preliminary commands it is necessary to use its -i option along with providing the preliminary commands as content of a bash script which file name is to specify using the long --rcfile option followed by the short -i option as the other way around just does not work probably because of the necessity to fulfill some POSIX standards. By the way: you may (haven't tested it yet) set a BASH_ENV environment variable before starting the shell instead of using the --rcfile option.

While what is stated above already fully answers your question let's give here as nice add-ons and for the sake of completeness also the Python code from the answer to your question provided by Charles Duffy and some further explanations given in the comments:

The code below is able to get the bash shell to do some preliminary work and then still stay alive in the Terminal when its job of resizing the terminal window and/or starting a Python session is done.

The code uses the more modern subprocess module instead of an os.system() call which (saying it with words by Charles Duffy) "creates an extra copy of /bin/sh, with all the extra complexities that come from needing an extra layer of escaping on top of your command (instead of being able to just pass the argument vector directly)". The code requires also the usage of the in Python distribution available tempfile module as a work around for creating an actual file bash then can get its preliminary commands from:

import tempfile, subprocess

with tempfile.NamedTemporaryFile(prefix='bash-rcfile') as tf:
  tf.write(b'resize -s 5 32\npython\n')
  tf.flush()
  subprocess.run(['gnome-terminal', '--', 'bash', '--rcfile', tf.name, '-i'])

The code above creates a Terminal window with an in it running interactive Python session and the special coded feature here is that when the Python session is exited, the Terminal window stays alive and open providing a bash shell prompt.

For the sake of completeness here also the explanation how it comes that the one line in your code runs ok without a space and the other does not (provided in the comments to the question by Socowi):

> Short answer: Every program decides how to interpret its options. Longer answer: Because every program decides on its own what to do with its options, -i can have an entirely different meaning between two different programs. They don't even have to accept options like -i but could even decide to use things like °:#§i__. Same goes for (optional) spaces. Complete Answer: The actual meaning of python -i and bash -i options can be found using ~ $ man python and ~ $ man bash. –
Socowi

By the way: instead of using resize to set the geometry of started Terminal window you can directly use the --geometry parameter supported by gnome-terminal setting at the same time not only the size, but also the position of the created Terminal window:

from tempfile   import NamedTemporaryFile as ntf
from subprocess import run
with ntf() as tf:
  tf.write(b'python\n') # commands for the job to be done by the shell
  tf.flush()
  run([ 'gnome-terminal', '--geometry=32x5+200+200', '--', 
            'bash', '--rcfile', tf.name, '-i'])

NOTICE in the code above that X,Y (i.e. width and height) specified in the --geometry option should be swapped compared to their order in resize -s

FINALLY The question What does the bash option -i actually do when bash is invoked from Python? would need much of further text for accurate explanations. One is sure: the -i alone isn't enough to make bash interactive. The code above provides a usage case for this option which does what was requested in the question. This should be enough as an answer.

huangapple
  • 本文由 发表于 2023年3月4日 02:25:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630631.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定