关于命令行交互自动化,及pyinstaller打包wexpect的问题

Python自动化工具

用来执行命令并进行交互,比如需要输入账号密码或者确认的场景

linux平台可以用pexpect,但是windows平台有一些差异,比较好用的是pexpect的变种wexpect,如果脚本中用了wexpect,并且要打包成onefile,可以

参考github首先打包wexpect

1.进入wexpect目录执行

pyinstaller main.py -n wexpect

会生成dist文件夹

2.python代码A.py中使用wexpect,注意wexpect.spawn前后必须按照下面添加代码

python 复制代码
import sys,os,wexpect
#spawn前
real_executable = sys.executable
try:
    if sys._MEIPASS is not None:
        sys.executable = os.path.join(sys._MEIPASS, "wexpect", "wexpect.exe")
except AttributeError:
    pass
child = wexpect.spawn('ssh root@192.168.47.128')
sys.executable = real_executable
#spawn后
try:
    child.expect('password',timeout=1)
    child.sendline("root@123")
    child.expect('#',timeout=1)
    child.sendline("touch wexpect")
    child.sendline("exit")
except Exception as e:
    print("timeout")
print('1.before = ',child.before)
print('2.after = ',child.after)
child.wait()

3.打包成onefile

pyinstaller --onefile --add-data "wexpect路径\dist;." A.py

Windows伪控制台

很少用到,只能参考windows官方文档自己写,启动伪控制台,并且通过管道直接和伪控制台交互,非常方便,传送门

自己随便写的demo如下:

cpp 复制代码
// PseudoConsoleDemo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <thread>
#include <string>
#include <codecvt>
#include <memory>
using namespace std;

void Log(string info) 
{
	//string cmd = "echo " + info;
	printf(info.c_str());
}

std::string UTF8ToLocale(const std::string& utf8Str)
{
	//Convert utf8 to Unicode
	std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
	std::wstring tmpWstr = conv.from_bytes(utf8Str);
	//Conver widestr to system code page
	int len = WideCharToMultiByte(CP_ACP, NULL, tmpWstr.c_str(), -1, NULL, NULL, NULL, NULL);
	char *str = new char[len];
	WideCharToMultiByte(CP_ACP, NULL, tmpWstr.c_str(), -1, str, len, NULL, NULL);
	return str;
}

HRESULT PrepareStartupInformation(HPCON hpc, STARTUPINFOEXW* psi)
{
	// Prepare Startup Information structure
	STARTUPINFOEXW si;
	ZeroMemory(&si, sizeof(si));
	si.StartupInfo.cb = sizeof(STARTUPINFOEX);

	// Discover the size required for the list
	SIZE_T bytesRequired;
	InitializeProcThreadAttributeList(NULL, 1, 0, &bytesRequired);

	// Allocate memory to represent the list
	si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, bytesRequired);
	if (!si.lpAttributeList)
	{
		return E_OUTOFMEMORY;
	}

	// Initialize the list memory location
	if (!InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &bytesRequired))
	{
		HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
		return HRESULT_FROM_WIN32(GetLastError());
	}

	// Set the pseudoconsole information into the list
	if (!UpdateProcThreadAttribute(si.lpAttributeList,
		0,
		PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
		hpc,
		sizeof(hpc),
		NULL,
		NULL))
	{
		HeapFree(GetProcessHeap(), 0, si.lpAttributeList);
		return HRESULT_FROM_WIN32(GetLastError());
	}

	*psi = si;

	return S_OK;
}
bool WritePipe(HANDLE pipeHd, const char DataBuffer[])
{
	DWORD dwBytesToWrite = (DWORD)strlen(DataBuffer);
	DWORD dwBytesWritten = 0;
	BOOL bErrorFlag = FALSE;



	bErrorFlag = WriteFile(
		pipeHd,           // open file handle
		DataBuffer,      // start of data to write
		dwBytesToWrite,  // number of bytes to write
		&dwBytesWritten, // number of bytes that were written
		NULL);
	return bErrorFlag;
}

bool ReadPipe(HANDLE pipeHd)
{
	const int BUFFERSIZE = 4096;
	char   ReadBuffer[BUFFERSIZE] = { 0 };

	if (pipeHd == INVALID_HANDLE_VALUE)
	{
		//printf(("Terminal failure: unable to open file \"\" for read.\n"));
		return false;
	}

	// Read one character less than the buffer size to save room for
	// the terminating NULL character. 
	DWORD dwBytesRead;
	DWORD dwBytesToRead = BUFFERSIZE - 1;
	
	if (FALSE == ReadFile(pipeHd, ReadBuffer, dwBytesToRead, &dwBytesRead, NULL))
	{

		//printf("Terminal failure: Unable to read from file.\n GetLastError=%08x\n", GetLastError());
		return false;
	}


	// This is the section of code that assumes the file is ANSI text. 
	// Modify this block for other data types if needed.

	if (dwBytesRead > 0 && dwBytesRead <= dwBytesToRead)
	{
		ReadBuffer[dwBytesRead] = '\0'; // NULL character

		//printf(("Data read from  (%d bytes): \n"), dwBytesRead);
		//PseudoConsole use utf8 encode
		Log(UTF8ToLocale(ReadBuffer));
		return true;
	}
	else if (dwBytesRead == 0)
	{
		//printf(("No data read from file\n"));
	}
	else
	{
		//printf("\n ** Unexpected value for dwBytesRead ** \n");
	}
	return false;
}
PWSTR String2PWSTR(const string& str)
{
	PWSTR pwstr;
	int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), NULL, 0);
	pwstr = new WCHAR[len + 1];
	MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), pwstr, len);
	pwstr[len] = '\0';
	return pwstr;
}
HPCON SetUpPseudoConsole(std::string cmd, HANDLE& read, HANDLE& write, PROCESS_INFORMATION& proc)
{
	COORD size = { 1000, 500 };
	HRESULT hr = S_OK;

	// Create communication channels

	// - Close these after CreateProcess of child application with pseudoconsole object.
	HANDLE inputReadSide, outputWriteSide;

	// - Hold onto these and use them for communication with the child through the pseudoconsole.
	HANDLE outputReadSide, inputWriteSide;

	if (!CreatePipe(&inputReadSide, &inputWriteSide, NULL, 0))
	{
		return NULL;
	}

	if (!CreatePipe(&outputReadSide, &outputWriteSide, NULL, 0))
	{
		return NULL;
	}

	HPCON hPC;

	hr = CreatePseudoConsole(size, inputReadSide, outputWriteSide, 0, &hPC);
	if (FAILED(hr))
	{
		return NULL;
	}
	STARTUPINFOEXW siEx;
	PrepareStartupInformation(hPC, &siEx);
	// Create mutable text string for CreateProcessW command line string.


	const string cmdStr = "C:\\windows\\system32\\cmd.exe /k \"echo off\"";
	PCWSTR childApplication = String2PWSTR(cmdStr);
	//PCWSTR childApplication = L"C:\\windows\\system32\\cmd.exe /k \"net use \\\\192.168.47.128\"";

	// Create mutable text string for CreateProcessW command line string.
	const size_t charsRequired = wcslen(childApplication) + 1; // +1 null terminator
	PWSTR cmdLineMutable = (PWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t) * charsRequired);

	if (!cmdLineMutable)
	{
		return NULL;
	}

	wcscpy_s(cmdLineMutable, charsRequired, childApplication);

	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(pi));

	// Call CreateProcess
	if (!CreateProcessW(NULL,
		cmdLineMutable,
		NULL,
		NULL,
		FALSE,
		EXTENDED_STARTUPINFO_PRESENT,
		NULL,
		NULL,
		&siEx.StartupInfo,
		&pi))
	{
		HeapFree(GetProcessHeap(), 0, cmdLineMutable);
		return NULL;
	}
	CloseHandle(inputReadSide);
	CloseHandle(outputWriteSide);
	//if (!WritePipe(inputWriteSide, "net use \\\\192.168.47.128\r")) {
	//	printf("write error");
	//}
	read = outputReadSide;
	write = inputWriteSide;
	proc = pi;
	return hPC;
}
int main()
{

	auto t = std::thread([]() {
		HANDLE read, write;
		PROCESS_INFORMATION pi;
		HPCON hpc = SetUpPseudoConsole("net use \\\\192.168.47.128", read, write, pi);
		DWORD exitCode;
		if (GetExitCodeProcess(pi.hProcess, &exitCode)) {
			//printf("get exit code success ,code=%d", exitCode);
			STILL_ACTIVE;
			if (exitCode == STILL_ACTIVE) {
				//printf("process alive");
			}
		}
		else {
			Log("get exit code failed");
		}

		if (!ReadPipe(read)) {
			Log("read error");
		}

		if (!WritePipe(write, "net use \\\\192.168.47.128\r")) {
			Log("write error");
		}
		if (!ReadPipe(read)) {
			Log("read error");
		}

		if (!WritePipe(write, "smbshare\r")) {
			Log("write error");
		}

		if (!ReadPipe(read)) {
			Log("read error");
		}
		if (!WritePipe(write, "smbshare\r")) {
			Log("write error");
		}

		if (!ReadPipe(read)) {
			Log("read error");
		}

		Sleep(100);
		if (GetExitCodeProcess(pi.hProcess, &exitCode)) {
			Log("get exit code success ,code=" + exitCode);
			STILL_ACTIVE;
			if (exitCode == STILL_ACTIVE) {
				Log("process alive");
			}
		}
		WaitForSingleObject(pi.hProcess, 0);
		WaitForSingleObject(pi.hThread, 0);
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
		ClosePseudoConsole(hpc);
		//TerminateProcess

	});
	t.join();
}
相关推荐
糊涂君-Q17 分钟前
Python小白学习教程从入门到入坑------第三十一课 迭代器(语法进阶)
python·学习·程序人生·考研·职场和发展·学习方法·改行学it
天飓23 分钟前
基于OpenCV的自制Python访客识别程序
人工智能·python·opencv
取个名字真难呐32 分钟前
矩阵乘法实现获取第i行,第j列值,矩阵大小不变
python·线性代数·矩阵·numpy
技术仔QAQ1 小时前
【tokenization分词】WordPiece, Byte-Pair Encoding(BPE), Byte-level BPE(BBPE)的原理和代码
人工智能·python·gpt·语言模型·自然语言处理·开源·nlp
WangYaolove13141 小时前
请解释Python中的装饰器是什么?如何使用它们?
linux·数据库·python
宋发元1 小时前
如何使用正则表达式验证域名
python·mysql·正则表达式
XMYX-02 小时前
Python 操作 Elasticsearch 全指南:从连接到数据查询与处理
python·elasticsearch·jenkins
正义的彬彬侠2 小时前
sklearn.datasets中make_classification函数
人工智能·python·机器学习·分类·sklearn
belldeep2 小时前
python:用 sklearn 转换器处理数据
python·机器学习·sklearn
安静的_显眼包O_o2 小时前
from sklearn.preprocessing import Imputer.处理缺失数据的工具
人工智能·python·sklearn