当我们用代码实现自己使用的小脚本时,构建GUI或者web前端明显是没必要的,在cmd中使用命令行快速调用会更加合适。但使用cmd 运行python有个很头疼的问题,运行.py脚本必须切换到python脚本所在的目录或提供脚本的绝对路径,这样并方便。我们今天讲一下如何解决这个问题。
先来看看 最终效果
runpy conut_code
你可以在任何地方运行你的python脚本。不管cmd当前在哪。也不用记忆绝对路径。
环境变量
安装完python后大家肯定接触过这个东西,当我们没有为python配置环境变量时,在命令行直接python会显示。python不是可执行文件。只有配置完后才可以调用python。环境变量记录了你的可执行文件的路径。当我们在cmd里运行时,系统会根据你配置的环境变量去找,如果找到就运行。当配置了环境变量就可以不提供完整路径去调用程序。
为什么.py脚本还需要完整路径?
.py文件其实并不是可执行文件。python.exe 才是,当我们调用python <脚本名称> 时其逻辑是运行python.exe 脚本名称只是提供给python.exe的一个参数而已。python.exe 读取你传的路径然后解释其中的内容然后运行。
代码的后续部分我们会慢慢讲。现在只需要看我箭头标注的地方。这行代码会打印python.exe的所有参数.
runpy.py只是个参数。真正的可执行文件是前面的python。这就是我们为什么需要提供完整路径。而无法通过环境变量来找到.py。
解决思路
runpy.bat
其实解决思路很简单。就是在python.exe 的调用上封装个可执行文件。
在window中我们可以选择.bat或.exe。由于我主要写python。写c还需要重新配置太麻烦。我们这里使用简单的.bat文件。
这个.bat文件,暂且跟着我的命名叫runpy.bat。后续大家可以修改。这里为了跟上我的思路暂时和我命名一样。
@echo off
python %~dp0\runpy.py %*
这个代码的意思很简单。不在详细讲。也没必要。大家不会去自己写bat脚本。这个代码的意思是运行于runpy.bat同一目录下的runpy.py文件。并且把
runpy.bat接受到的所有参数都传递给runpy.py。.bat文件是可执行文件。把你的runpy.bat所在的路径添加到环境变量,就可以在cmd中直接通过runpy 来运行。
runpy.py
runpy.py 负责解析参数来运行最终的.py文件。注意 因为 runpy.bat写的是同一路径下的runpy.py 所以要把他们放在一起。
print("我是python")
为了检验,你的.bat是否正常我们先写个简单的逻辑。打印一句话。
然后你可以在任何路径下运行runpy。来运行文件。这里的逻辑是我们runpy。环境变量帮我们找到runpy.bat。runpy.bat里调用的是runpy.py
很好如果你成功了。我们可以开始下一步就完善runpy.py
事实上我们是在模拟环境变量的事情。
还是这个路径添加一个script_paths.txt文件。像环境变量一样放脚本路径。
if __name__ == "__main__":
from sys import argv
the_current_path = os.path.dirname(argv[0])
script_paths_file = os.path.join(the_current_path, "script_paths.txt")
需要注意的是我们想要获取script_paths.txt 并不能通过"./“这个相对路径。此时用”./"获取的是何处调用了这个文件的路径而不是runpy.py 的目录.
if __name__ == "__main__":
from sys import argv
the_current_path = os.path.dirname(argv[0])
script_paths_file = os.path.join(the_current_path, "script_paths.txt")
#完整路径
print("当前路径"+os.path.abspath("./"))
打印当前路径
是我们何处调用了脚本。我们需要获取runpy的路径需要
the_current_path = os.path.dirname(argv[0])
这行代码为什么可以获取路径?
看.bat文件。
```bash
@echo off
python %~dp0\runpy.py %*
这里和python一样。所有相对路径都是调用时的路径。所以即使。runpu.bat
和runpy.py在同一个路径也无法相对路径调用。而%~dp0就是可以获取runpu.bat所在的绝对路径。我们一开始就讲了。python才是执行文件后面的是参数
%~dp0\runpy.py就是传给python的第一个参数。我们就可以通过argv[0]获取第一个参数。os.path.dirname用于获取一个路径的目录部分。比如我们D:/doc/runpy.py os.path.dirname就会返回D:/doc/ 这样我们就可以获取runpy.py的目录。然后获取script_paths.txt文件。
if __name__ == "__main__":
from sys import argv
the_current_path = os.path.dirname(argv[0])
script_paths_file = os.path.join(the_current_path, "script_paths.txt")
#完整路径
#print("当前路径"+os.path.abspath("./"))
if argv[1] == "-add":
with open(script_paths_file, "a") as f:
f.write(argv[2] + "\n")
print("添加成功")
elif argv[1] == "-del":
with open(script_paths_file, "r") as f:
script_paths = [line.strip() for line in f.readlines()]
if argv[2] not in script_paths:
print("脚本不存在")
exit()
with open(script_paths_file, "w") as f:
for script_path in script_paths:
if script_path != argv[2]:
f.write(script_path + "\n")
print("删除成功")
elif argv[1] == "-all":
print("\n打印所有脚本路径")
with open(script_paths_file, "r") as f:
script_paths = [line.strip() for line in f.readlines()]
for script_path in script_paths:
print(script_path)
后部面的部分就要根据参数来执行命令了。这里我们传递给runpy的参数都会传给runpy.py然后runpy.py的路径也个参数。这里讲了三遍了还不理解我就疯了。所以我们后面的判断要从argv[1]开始而不是argv[0]。
我们写了三个参数分支。用于添加环境变量。就是简单的读写文件。
测试一下。
from sys import argv
the_current_path = os.path.dirname(argv[0])
script_paths_file = os.path.join(the_current_path, "script_paths.txt")
#完整路径
# print("当前路径"+os.path.abspath("./"))
if argv[1] == "-add":
with open(script_paths_file, "a") as f:
f.write(argv[2] + "\n")
print("添加成功")
elif argv[1] == "-del":
with open(script_paths_file, "r") as f:
script_paths = [line.strip() for line in f.readlines()]
if argv[2] not in script_paths:
print("脚本不存在")
exit()
with open(script_paths_file, "w") as f:
for script_path in script_paths:
if script_path != argv[2]:
f.write(script_path + "\n")
print("删除成功")
elif argv[1] == "-all":
print("\n打印所有脚本路径")
with open(script_paths_file, "r") as f:
script_paths = [line.strip() for line in f.readlines()]
for script_path in script_paths:
print(script_path)
else:
py_name = argv[1]
if not py_name.endswith(".py"):
py_name += ".py"
if "/" in py_name:
py_name = py_name.replace("/", "\\")
path = locate_the_script(py_name)
args=""
for i in argv[2:]:
args+=i+" "
os.system(f"python {
path} {
args}")
print("执行完毕")
最后一个分支判断就是开始真正的执行我们想要的路径了。
也没啥说的。就是找到传进来的脚本名称的绝对路径然后在拼接个python执行就行。同样的把需要的参数传给python。
def locate_the_script(script_name):
with open(script_paths_file , "r") as f:
script_paths = [line.strip() for line in f.readlines()]
if not script_paths:
print("您必须指定一个路径")
print(r"比如您的python脚本写在D:\script下")
print(r"调用runpy -add D:\script")
exit()
for script_path in script_paths:
for script in find_script(script_path):
if script.endswith(script_name):
return script
exit("没有找到脚本")
这里只是提供了一个解决这个问题的思路。没仔细写。找路径的算法直接是循环。大家可以凭自己的技术实现快速找到路径的功能。比如用缓存技术。试试用数据库。换个厉害的搜索算法。
用法
我们可以在任何路径下编写python脚本。将目录用上文的 -add添加到我们的文件里。然后你就可以在任何地方。运行你的脚本。还不用记忆绝对路径。如果你的脚本名字取的好。那调用脚本就想和计算机说话一样。
最后展示这个脚本的便利。
我们可以随地调用,还不用管绝对路径。那么用cmd就是对话一样。比如一个统计代码行数的脚本。 在你需要统计代码行数的文件夹下。
(这个统计代码行数的脚本,并不复杂。只是觉得放在这个文章里不妥。不再给出,大家可以尝试写自己的脚本。来执行。)
右键,我们可以在终端打开。
就可以统计当前目录下的代码行数。并且命令可以说十分简单,还不用记录.py的路径。随地调用你的脚本。