从标准输入读命令并执行

实例最好用

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

#define MAXLEN 4096

int main(void)
{
    char buf[MAXLEN];
    pid_t pid;
    int status;
    
    printf("%%");
    while(fgets(buf,MAXLEN,stdin) != NULL) {
        if(buf[buf(strlen(buf)-1)] == '\n') {
            buf[strlen(buf)-1] = 0;
        }
        if(pid = fork() < 0) {
            err_sys("fork error");
        }else if(pid == 0) {
            execlp(buf,buf,(char*)0);
            err_ret("couldn't execute: %s",buf);
            exit(127);
        }
        
        if(pid = waitpid(pid,&status,0) < 0) {
            err_sys("waitpid error");
        }
        printf("%%");
    }
    exit(0);
}

执行结果示例:

$ ./a.out
% date
2019年 03月 10日 星期日 09:16:01 CST
% who
cb    :0           2019-03-02 22:14 (:0)
% pwd
/home/cb/Unix/1_part
% ^D            键入文件结束符
$               

顺便学习一下execlp的用法

execlp
从PATH 环境变量中查找文件并执行

定义:
int execlp(const char * file,const char * arg,……);

头文件:
#include<unistd.h>

说明:
execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束。

返回值:
如果执行成功则函数不会返回, 执行失败则直接返回-1, 失败原因存于errno 中。
 

<think>我们在Python中执行CMD命令与其进行交互(即向其输入值)通常可以使用`subprocess`模块。这个模块提供了更强大和灵活的方式来创建子进程,执行命令处理输入输出流。 常见的方法: 1. 使用`subprocess.run()`:适用于较新版本的Python(3.5+),适合简单的交互或不需要交互的情况。 2. 使用`subprocess.Popen`:更底层的接口,适用于需要复杂交互的情况,比如需要向进程的标准输入(stdin)写入数据,同时取标准输出(stdout)和标准错误(stderr)。 由于用户需要向命令提供输入值,这里我们需要使用`Popen`来操作。 步骤: a. 使用`subprocess.Popen`创建进程,指定命令设置stdin, stdout, stderr为管道(PIPE)以便进行交互。 b. 使用`communicate()`方法向进程的标准输入发送数据,获取输出和错误。 注意:`communicate()`会等待进程结束,且一次性取所有输出。如果进程需要多次交互(即多次输入和输出),则需要分步写入和取,但要注意避免死锁。 示例:执行一个需要输入的命令(例如,一个简单的程序,要求输入名字,然后输出问候语) 假设我们有一个命令`greet.cmd`(Windows批处理)或一个程序,它会要求输入,然后输出。但这里我们用一个简单的例子:在Python中执行`sort`命令(Windows和类Unix系统都有,但行为稍有不同),我们向它输入几行文字,然后它会返回排序后的结果。 但注意:在Windows上,我们通常使用`cmd.exe /C`来执行命令(如引用[2]中提到的陷阱4,我们需要根据系统调整命令的调用方式)。不过,`subprocess`模块在Windows上也可以直接执行程序,对于内置命令(如`dir`、`copy`)通常需要指定`shell=True`或者通过`cmd.exe /C`来执行。 然而,对于需要输入的命令,我们通常直接执行执行文件(比如`python`,`sort`等),而不一定是通过`cmd`。如果确实是CMD内部命令,我们可以通过`cmd.exe /C`来执行。 示例1:执行一个需要输入的命令(例如,执行`python -c "print(input('Enter something: '))"`,这个命令会要求用户输入,然后打印出来) 但我们希望自动化输入,即用程序代替人工输入。 步骤: 1. 导入subprocess 2. 创建Popen对象,指定命令,设置stdin, stdout, stderr为PIPE 3. 使用communicate(input=...)来发送输入(注意输入需要是字节串,所以需要encode) 示例代码: ```python import subprocess # 命令:启动一个Python解释器,执行一段代码,该代码会要求输入 cmd = ['python', '-c', "print('Enter your name: '); name = input(); print('Hello', name)"] # 启动进程 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 我们准备输入的数据,注意需要编码为字节串 input_data = "Alice" # 要输入的内容 # 使用communicate发送输入,获取输出和错误 # communicate会等待进程结束 stdout, stderr = proc.communicate(input=input_data.encode()) # 打印输出 print("标准输出:", stdout.decode()) print("标准错误:", stderr.decode()) ``` 但是注意:上述代码中,我们写的Python程序会先打印提示,然后等待输入。我们通过`communicate`一次性将输入数据`"Alice"`发送给子进程。 如果命令需要多次输入,使用`communicate`可能就不太方便,因为`communicate`只能进行一次输入。这时,我们需要分步操作: 1. 创建进程后,使用`proc.stdin.write`多次写入,但要小心缓冲区问题。 2. 同时,要取输出时需要避免阻塞(通常使用异步或轮询的方式)。 但这样容易导致死锁(例如,子进程在等待输入,而父进程在等待子进程输出,而输出可能被缓冲)。因此,对于复杂的交互,建议使用`pexpect`模块(在Unix-like系统上)或`wexpect`(Windows)模块。 不过,这里我们主要讨论`subprocess`。 示例2:多次交互(以Unix的`bc`计算器为例,在Windows上可能没有,所以这里我们用Python模拟一个需要多次输入的程序) 假设我们有一个程序,它要求输入两个数字,然后输出和,然后再要求输入两个数字,再输出和,共两次。 我们可以这样: ```python import subprocess cmd = ['python', '-c', "print('First number:'); a=input(); print('Second number:'); b=input(); print('Sum1:', int(a)+int(b)); " + "print('Third number:'); c=input(); print('Fourth number:'); d=input(); print('Sum2:', int(c)+int(d))"] proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 我们分两次发送输入,但注意:由于输入是缓冲的,我们可以分多次写入,但要记得关闭stdin以便子进程知道输入结束 # 但是,如果我们使用communicate,它只能一次。所以这里我们不用communicate,而是手动写入和取。 # 注意:手动操作时,要注意避免死锁。通常,我们可以在写入后关闭stdin,然后取stdout和stderr直到结束。 # 但如果我们需要多次交互(即写入-取-写入-取),那么我们需要在写入后,取输出,然后再写入。这需要非阻塞取或使用线程。 # 对于简单的两次输入,我们可以一次性写入所有输入(因为程序会按顺序取),所以我们可以将所有的输入数据用换行符连接起来,然后一次发送。 # 例如,输入:1, 2, 3, 4 -> 程序会先1(第一个数),再2(第二个数),然后输出和,再3,再4,再输出和。 input_str = "1\n2\n3\n4\n" # 注意每个输入后面都要有换行符,因为input()是按行取的 # 然后我们使用communicate stdout, stderr = proc.communicate(input=input_str.encode()) print("输出:", stdout.decode()) ``` 然而,如果程序在两次输入之间需要根据输出决定下一次输入(即交互式),那么我们就不能一次性发送所有输入。这时,我们需要逐次写入和取。 示例3:逐次交互(以简单的两次输入为例,但第二次输入需要根据第一次的输出决定) 由于逐次交互容易死锁,我们通常使用`select`模块来监控管道(或者使用多线程)。但这里为了简单,我们假设输出不会太大,我们可以每次取一行。 注意:这仅适用于行缓冲的情况,如果输出不是行缓冲,可能会阻塞。 步骤: 1. 创建进程 2. 写入第一行输入 3. 取输出直到下一个输入点(例如,取到提示符) 4. 写入第二行输入 5. 重复直到完成 6. 关闭stdin,取剩余输出 代码示例(以两个数的加法为例,但程序会在每次输入前打印提示): ```python import subprocess import sys cmd = ['python', '-c', "print('First number:'); a = input(); print('Second number:'); b = input(); print('Sum:', int(a)+int(b))"] proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, # 设置文本模式,这样我们可以按字符串写,但注意Python3.7以上才支持encoding参数 # 为了兼容性,我们使用字节流,然后自己解码 ) # 我们定义一个函数来取输出直到提示(这里我们按行取) def read_until_prompt(proc, prompt): """取直到遇到提示字符串""" output = [] while True: # 每次一个字符,直到遇到换行?这样效率低,但简单做法是一行 # 注意:如果提示不在行尾,我们需要更复杂的逻辑,这里假设提示是一行 line = proc.stdout.readline().decode() if not line: # 无数据,进程可能结束 break output.append(line) if prompt in line: break return ''.join(output) # 第一次交互 # 等待第一个提示:'First number:' # 取一行应该就能得到 first_prompt = "First number:" # 取直到出现第一个提示 output1 = read_until_prompt(proc, first_prompt) print("第一次提示的输出:", output1) # 现在输入第一个数字 proc.stdin.write("10\n".encode()) proc.stdin.flush() # 确保发送 # 第二次交互:等待第二个提示 second_prompt = "Second number:" output2 = read_until_prompt(proc, second_prompt) print("第二次提示的输出:", output2) # 输入第二个数字 proc.stdin.write("20\n".encode()) proc.stdin.flush() # 然后,我们取剩余的输出(直到进程结束) # 注意:在写入后,我们需要关闭stdin(如果进程需要)取剩余输出 proc.stdin.close() remaining_output = proc.stdout.read().decode() print("剩余输出:", remaining_output) # 等待进程结束 proc.wait() ``` 但是,上述代码在实际情况中可能会遇到问题,因为子进程的输出可能被缓冲,导致`readline()`阻塞。为了减少缓冲,可以在子进程中使用`-u`参数(对于Python)来禁用缓冲,或者设置环境变量`PYTHONUNBUFFERED=1`。 另外,对于其他程序,如果它们没有使用行缓冲,那么我们需要考虑更复杂的方法。 因此,总结一下: 1. 对于简单的输入,使用`communicate`一次性发送所有输入。 2. 对于需要多次输入且输入顺序固定(不需要根据输出决定输入)的情况,也可以一次性发送所有输入(用换行符分隔)。 3. 对于复杂的交互(需要根据输出决定输入),则使用分步写入和取,注意缓冲问题。 另外,在Windows上,如果执行的是内部命令(如dir),我们需要通过`cmd.exe /C`来执行,例如: ```python cmd = ['cmd.exe', '/C', 'dir'] ``` 然后同样使用`subprocess.Popen`来执行。 最后,关于引用: 引用[2]中提到,在Java中执行命令时,需要根据系统版本调整命令调用方式。在Python中,我们通常使用`subprocess`模块,它内部已经处理了很多平台差异,但对于命令本身,我们可能仍需要根据系统调整命令字符串。 例如,在Windows上执行dir,我们可以: ```python import subprocess import sys if sys.platform == 'win32': cmd = ['cmd.exe', '/C', 'dir'] else: cmd = ['ls', '-l'] # 然后执行cmd ``` 但是,如果我们使用`shell=True`,则可以直接传入命令字符串,例如: ```python subprocess.Popen('dir', shell=True) # 在Windows上 subprocess.Popen('ls -l', shell=True) # 在Unix上 ``` 使用`shell=True`可以让系统shell解释命令,但要注意安全风险(如果命令字符串来自用户输入,可能有命令注入风险)。 因此,根据需求选择合适的方式。 相关问题: 1. 如何在Python中执行带管道的命令(例如`dir | findstr "txt"`)? 2. 在Python中执行命令时,如何避免死锁? 3. 如何实时获取命令的输出(例如,显示进度条)? 4. 在Windows和Linux上执行命令有何不同? 5. 如何使用`subprocess`模块捕获标准错误与标准输出合?</think>在Python中执行CMD命令向其提供输入值,可以使用`subprocess`模块。以下是详细方法和示例代码: ### 方法1:使用`subprocess.run()`(Python 3.5+) ```python import subprocess # 执行命令自动传递输入值 result = subprocess.run( ["python", "-c", "print('Input:'); x=input(); print(f'Received: {x}')"], input="Hello Python", # 输入值(字符串) text=True, # 启用文本模式(自动编解码) capture_output=True # 捕获输出 ) print("输出结果:", result.stdout) ``` ### 方法2:使用`subprocess.Popen()`(复杂交互) ```python import subprocess # 启动进程 proc = subprocess.Popen( ["python", "-c", "print('Input:'); x=input(); print(f'Received: {x}')"], stdin=subprocess.PIPE, # 启用输入管道 stdout=subprocess.PIPE, # 捕获输出 stderr=subprocess.PIPE, text=True # 文本模式 ) # 发送输入值获取输出 output, errors = proc.communicate(input="Hello Popen") # 输入值 print("输出结果:", output) ``` ### 关键参数说明 1. **`input`**:向命令传递输入值(字符串或字节) 2. **`text=True`**:自动处理字符串编解码(Python 3.7+) 3. **`capture_output=True`**:捕获标准输出/错误(`subprocess.run`) 4. **`stdin=subprocess.PIPE`**:启用输入管道(`Popen`) 5. **`communicate()`**:安全传递输入获取输出 ### 跨平台注意事项 1. **Windows特殊命令**(如`dir`)需通过`cmd.exe`执行: ```python subprocess.run(["cmd", "/C", "dir"], capture_output=True, text=True) ``` 2. **路径空格处理**:使用原始字符串或双引号 ```python subprocess.run(r"C:\Program Files\app.exe", shell=True) ``` ### 常见问题解决 1. **命令无响应**:检查是否需要换行符`\n`结尾 ```python proc.communicate(input="value\n") # 添加换行符 ``` 2. **超时控制**: ```python try: result = subprocess.run(..., timeout=5) # 5秒超时 except TimeoutExpired: print("命令执行超时") ``` 3. **二进制输入**(如图片): ```python with open("image.png", "rb") as f: subprocess.run(["process_img"], input=f.read()) ``` > **注意**:直接拼接用户输入到命令中可能导致命令注入漏洞,需使用参数列表而非字符串: > ```python > # 危险方式(可能注入) > subprocess.run(f"echo {user_input}", shell=True) > > # 安全方式 > subprocess.run(["echo", user_input]) > ``` ### 应用场景示例 ```python # 自动化MySQL登录执行查询 sql_script = """ SELECT * FROM users; EXIT; """ result = subprocess.run( ["mysql", "-u", "root", "-p"], input=sql_script, text=True, capture_output=True ) print("查询结果:", result.stdout) ``` [^1]: 在Python中执行命令时,系统调用延迟通常在毫秒级,但实际性能受环境配置影响 [^2]: Windows平台执行内部命令需通过`cmd.exe /C`或`command.com`封装 [^3]: 使用`text=True`参数可避免手动处理编解码问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值