[英]In python how can I take input from user and jump to a different line after taking input?
[英]How can I take piped input from another application AND take user input after?
import sys
stdin_input = sys.stdin.read()
print(f"Info loaded from stdin: {stdin_input}")
user_input = input("User input goes here: ")
收到錯誤:
C:\>echo "hello" | python winput.py
Info loaded from stdin: "hello"
User input goes here: Traceback (most recent call last):
File "C:\winput.py", line 6, in <module>
user_input = input("User input goes here: ")
EOFError: EOF when reading a line
我最近了解到這是因為sys.stdin
被用於 FIFO,這使得它在閱讀后關閉。
我可以根據這個問題在stdin_input = sys.stdin.read()
之后添加sys.stdin = open("/dev/tty")
使其在 CentOS 上運行,但這不適用於 Windows。
最好不要識別操作系統並相應地為sys.stdin
分配新值,我寧願動態地處理它。 有沒有一種方法可以確定在每種情況下 /dev /dev/tty
tty 的等價物是什么,而不必知道/dev/tty
或特定操作系統的等價物?
編輯:
sys.stdin.read()
的原因是接收來自另一個應用程序的 JSON 輸入。 我還可以選擇從文件中讀取 JSON 數據,但能夠使用管道數據非常方便。 收到數據后,我想單獨獲取用戶輸入。
我目前正在解決以下問題:
if os.name == "posix":
sys.stdin = open("/dev/tty")
elif os.name == "nt":
sys.stdin = open("con")
else:
raise RunTimeError(
f"Error trying to assign to sys.stdin due to unknown os {os.name}"
)
這在所有情況下都可以很好地工作,但仍然最好知道/dev/tty
或con
或操作系統的任何等效項是動態的。 如果這是不可能的,而我的解決方法是最好的解決方案,我可以接受。
所以你真正的問題是sys.stdin
只能是兩件事之一:
通過執行sys.stdin.read()
消耗所有sys.stdin
並不重要,當sys.stdin
被重定向到某個文件系統 object 時,您失去了通過sys.stdin
從終端讀取的能力。
實際上,我強烈建議不要嘗試這樣做。 使用argparse
並接受您正在考慮通過命令行input
接受的任何內容並避免整個問題(實際上,我基本上從未見過真正的生產代碼不是通過stdin
/ stdout
交互與用戶動態交互的某種 REPL;對於在非 REPL 情況下, sys.stdin
基本上總是未使用或從文件/程序中通過管道傳輸,因為像這樣編寫干凈的用戶交互代碼是一種痛苦,而且用戶必須在不出錯的情況下鍵入他們的響應是一種痛苦). 可以通過將type=argparse.FileType()
add_argument
stdin
來處理可能來自文件或標准輸入的輸入,然后用戶可以選擇傳遞文件名或-
(其中-
表示“讀取自stdin"), 讓你的代碼看起來像:
parser = argparse.ArgumentParser('Program description here')
parser.add_argument('inputfile', type=argparse.FileType(), help='Description here; pass "-" to read from stdin')
parser.add_argument('-c', '--cmd', action='append', help='User commands to execute after processing input file')
args = parser.parse_args()
with args.inputfile as f:
data = f.read()
for cmd in args.cmd:
# Do stuff based on cmd
然后用戶可以執行以下操作:
otherprogram_that_generates_data | myprogram.py - -c 'command 1' -c 'command 2'
或者:
myprogram.py file_containing_data -c 'command 1' -c 'command 2'
或(在具有進程替換的 shell 上,如bash
,作為第一個用例的替代方案):
myprogram.py <(otherprogram_that_generates_data) -c 'command 1' -c 'command 2'
無論哪種方式,它都有效。
如果您必須這樣做,您現有的解決方案確實是唯一合理的解決方案,但您可以將其分解並使其更清晰一些,只使路徑動態化,而不是整個代碼路徑:
import contextlib
import os
import sys
TTYNAMES = {"posix": "/dev/tty", "nt": "con"}
@contextlib.contextmanager
def stdin_from_terminal():
try:
ttyname = TTYNAMES[os.name]
except KeyError:
raise OSError(f"{os.name} does not support manually reading from the terminal")
with open(ttyname) as tty:
sys.stdin, oldstdin = tty, sys.stdin
try:
yield
finally:
sys.stdin = oldstdin
如果在沒有連接終端的情況下運行,這可能會在open
調用中死於OSError
子類,例如,當在 Windows 上使用pythonw
啟動時(不使用此設計的另一個原因),或者在類 UNIX 上以非終端方式啟動,但那是總比默默地犯錯好。
您只需使用它:
with stdin_from_terminal():
user_input = input("User input goes here: ")
並且它會在退出with
塊時自動恢復原始sys.stdin
。
由於您使用的是 Bash ,您可以通過使用進程替換來避免此問題,它類似於 pipe,但通過臨時文件名參數而不是通過標准輸入傳遞。
那看起來像:
winput.py <(another-application)
然后在您的 Python 腳本中,接收參數並相應地處理它:
import json
import sys
with open(sys.argv[1]) as f:
d = json.load(f)
print(d)
user_input = input("User input goes here: ")
print('User input:', user_input)
( sys.argv
僅用於演示。在真實腳本中我會使用argparse
。)
示例運行:
$ tmp.py <(echo '{"someKey": "someValue"}')
{'someKey': 'someValue'}
User input goes here: 6
User input: 6
另一個巨大的優勢是它可以與實際文件名無縫協作,例如:
$ cat test.json
{"foo": "bar"}
$ tmp.py test.json
{'foo': 'bar'}
User input goes here: x
User input: x
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.