python包-subprosscess及f-string用法

介绍 subprocess 和 f-string 的常用的用法。

subprocess

subprocess 模块允许你生成新的子进程,连接它们的输入,输出,错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能,比如 os.system() 。

subprocess.run()

推荐的调用子进程的方式是在任何它支持的用例中使用 run() 函数(python 3.5及以上,官方推荐)。对于更进阶的用例,也可以使用底层的 Popen 接口。

参数

使用的参数入下,常用参数为 shell, stdout, stderr, encoding

1
subprocess.run(args, *,  stdout=None, stderr=None, shell=False, check=False, timeout=None,encoding=None )
  • args: 要执行的shell命令,默认应该是一个字符串序列,如[‘df’, ‘-Th’]或(‘df’, ‘-Th’)。如果如果传递单个字符串,shell 参数必须为True(推荐使用)

  • shell: 如果shell为True,那么指定的命令将通过shell执行。

  • stdin, stdout, stderr:

    进程的标准输入、输出和错误,默认为 None (表示什么也不做,输出直接打印到屏幕上), 其值可以是subprocess.PIPE (管道)、subprocess.DEVNULL(这个估计类似于 > /dev/null )、一个已经存在的文件描述符、已经打开的文件对象或者None。

    如果设置了参数stderr=subprocess.STDOUT,则错误信息会和stdout一起输出,此时stderr的返回值是None。

  • encoding: 如果指定了该参数,则stdin、stdout和stderr可以接收字符串数据,并以该编码方式编码。否则只接收bytes类型的数据

  • check: 如果check参数的值是True,且执行命令的进程以非0状态码退出,则会抛出一个CalledProcessError的异常。

  • timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出TimeoutExpired异常。

输出

输出是一个CompletedProcess类型对象,具有以下属性

  • args 启动进程的参数,通常是个列表或字符串。
  • returncode 进程结束状态返回码。0表示成功状态。
  • stdout 获取子进程的stdout。
  • stderr 获取子进程的错误信息,正常为 None 。
  • check_returncode() 用于检查返回码。如果返回状态码不为零,弹出CalledProcessError异常。

简单例子

首先,查看 args 参数 和 shell 参数,建议使用第二种方式,更加方便,而且可以采用 f-string 的方式加入参数。

1
2
3
In [16]: subprocess.run("python --version", shell=True) # 推荐
Python 3.9.10
Out[16]: CompletedProcess(args='python --version', returncode=0)

查看 check 参数,就是如果 check = True ,那么如果有问题就会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
In [17]: a = subprocess.run("exit 1", shell=True)

In [18]: a.returncode # returncode 不为 0
Out[18]: 1

In [19]: a = subprocess.run("exit 1", shell=True, check=True)
---------------------------------------------------------------------------
CalledProcessError Traceback (most recent call last)
Input In [19], in <module>
----> 1 a = subprocess.run("exit 1", shell=True, check=True)

File /mnt/data/zhouziwen/lib/miniconda3/lib/python3.9/subprocess.py:528, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
526 retcode = process.poll()
527 if check and retcode:
--> 528 raise CalledProcessError(retcode, process.args,
529 output=stdout, stderr=stderr)
530 return CompletedProcess(process.args, retcode, stdout, stderr)

CalledProcessError: Command 'exit 1' returned non-zero exit status 1.

获取执行结果

stdout=subprocess.PIPE

需要将 stdout 设为 subprocess.PIPE, 然后获取输出值的 stdout 属性,推荐同时使用 encoding 参数

不设置 encoding 参数, 需要使用 decode(‘utf-8’) 方法转为字符串。

1
2
3
4
In [22]: a=subprocess.run("wc -l all.ped", shell = True,  stdout = subprocess.PIPE).stdout.decode('utf-8').split()[0]

In [23]: a
Out[23]: '773'

设置 encoding 参数 ,看上去更加清爽一点。

1
2
3
4
5
6
In [45]: a = subprocess.run(
...: "wc -l all.ped", shell=True, encoding="utf-8", stdout=subprocess.PIPE
...: ).stdout

In [46]: a
Out[46]: '773 all.ped\n'

举个输出为多行字符串的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
In [47]: a = subprocess.run(
...: "wc -l *.ped", shell=True, encoding="utf-8", stdout=subprocess.PIPE
...: ).stdout

In [48]: a
Out[48]: ' 773 all.ped\n 193 1.ped\n 198 2.ped\n 187 3.ped\n 1351 total\n'

In [49]: a.splitlines()
Out[49]:
[' 773 all.ped',
' 193 1.ped',
' 198 2.ped',
' 187 3.ped',
' 1351 total']

如果含有参数,使用 f-string 语法

1
2
3
4
5
6
7
8
9
10
In [26]: filename = "all.ped"

In [27]: a = (
...: subprocess.run(f"wc -l {filename}", shell=True, stdout=subprocess.PIPE)
...: .stdout.decode("utf-8")
...: .split()[0]
...: )

In [28]: a
Out[28]: '773'

stdout=filename

输出为文件

1
2
3
4
5
6
7
8
9
10
11
12
13
In [41]: test_file = open("test.txt", "w")

In [42]: a=subprocess.run("wc -l *.ped", shell = True, encoding = "utf-8", stdout = test_file)

In [43]: test_file.close()

In [44]: !cat test.txt
773 all.ped
193 1.ped
198 2.ped
187 3.ped
1351 total

subprocess.Popen()

用法基本和 run() 方法类同,但是返回的是一个 Popen 对象,其 stdin、stdout、stderr 是三个文件句柄,可以像文件那样进行读写操作。

1
2
3
4
In [30]: a = subprocess.Popen("wc -l *.ped", shell=True, stdout=subprocess.PIPE, encoding="utf-8")

In [31]: a.stdout.readline()
Out[31]: ' 773 all.ped\n'

也可以写入一个文件

1
2
3
4
5
6
7
8
9
10
11
12
In [32]: test_file = open("test.txt", "w")

In [33]: a = subprocess.Popen("wc -l *.ped", shell=True, stdout=test_file, encoding="utf-8")

In [34]: test_file.close()

In [35]: !cat test.txt
773 all.ped
193 1.ped
198 2.ped
187 3.ped
1351 total

f-string 技巧

f-string 是Python3.6新引入的一种字符串格式化方法,上面 subprocess 的介绍中也提到了 f-string ,其使用起来非常简便。其形式就是 f'***'F'***' (单双引号均可),以大括号{}表示被替换的字段(也可以是表达式或待用函数)。

特殊字符

单双引号混用

f-string 如果使用了双引号,则字符串中则不能再出现双引号(出现会报错),如果需要使用,需要用反斜杠 \ 去除特殊含义,单引号同理。

1
2
3
4
5
6
7
8
9
In [53]: f"a" b "" # 报错
Input In [53]
f"a" b "" # 报错
^
SyntaxError: invalid syntax


In [54]: f'a" b "'
Out[54]: 'a" b "'

一种简单的解决方法是,如果字符串中只出现双引号,那么 f-string 就可以使用单引号,这样就不用使用反斜杠,反之同理。

1
2
In [54]: f'a" b "'
Out[54]: 'a" b "'

如果字符串中内单双引号混用, f-string 可以使用 f""""""f'''''',这种三引号的格式 (这种格式还可以支持多行的字符串) 。

1
2
In [55]: f''' I'am very "good" at sth  '''
Out[55]: ' I\'am very "good" at sth '

注意这里我没有考虑 {} 中还有引号的情况,因为被替换的字段包含引号的情况基本不可能发生。

大括号

如果 f-string 中需要使用大括号,则需要输入连续两个大括号 {{` 和 `}}

举个实际的例子,如果我们使用 awk 获取某个文件的第二列,则可以使用以下命令。

1
2
3
In [57]: filename = "all.ped"

In [58]: a = subprocess.run(f"awk '{{print $2}}' {filename}", shell=True, encoding = "utf-8", stdout=subprocess.PIPE)

自定义格式

f-string采用 {content:format} 设置字符串格式,其中 content 是替换并填入字符串的内容,可以是变量、表达式或函数等,format 是格式描述符。如果不指定 {:format} 则采用默认格式。

对齐字符串

“<” 表示左对齐, “>” 表示右对齐,“^" 表示居中。

举例如下

1
2
3
4
In [67]: b = "abcdef"

In [68]: f"{b:<8}"
Out[68]: 'abcdef '

宽度和精度

这里只介绍最常用的宽度与精度的格式。一般格式为 width.precision, 整数 width 指定宽度(宽度表示输出的最小字符数,不足用空格填充),整数 precision 指定显示精度(用于设置浮点数小数部分的位数)。

width.precision 用于 f 和 % (浮点数) 时 precision 指定的是小数点后的位数。

width.precision 用于字符串时 precision 含义是只使用字符串中前 precision 位字符。

浮点数

我最常用的就是设置小数点位数,这里 width 设为 0 。

使用 % 来获得百分号的格式。

1
2
3
4
5
6
7
In [74]: a = 111.111

In [75]: f"{a:0.2f}" # 2 位小数
Out[75]: '111.11'

In [76]: f"{a:0.2%}" # 百分数
Out[76]: '11111.10%'

省略 width 也可以(更通用的写法是省略, 等价于 round() 函数)

1
2
In [83]: f"{a:.2f}" # 2 位小数
Out[83]: '111.11'

但是注意双精度下,只能保留 16 位小数(只能精确到小数点后 15 位,有效位数位 16位 )。

1
2
3
4
5
6
7
In [93]: a = 1.12345678901234567890

In [94]: a
Out[94]: 1.1234567890123457

In [95]: f"{a:.18f}"
Out[95]: '1.123456789012345691'

设置有效数字

上面是设置小数点位数,如果是设置有效数字数目,则将 .2f 改为 .2g

1
2
3
4
5
6
7
In [20]: number = 314.25588

In [23]: f"{number:.5g}"
Out[23]: '314.26'

In [28]: f"{number:0.5f}"
Out[28]: '38.33334'

字符串

没怎么格式化字符串

1
2
3
4
5
6
7
8
In [78]: f"{a:8s}"
Out[78]: 'hell0 '

In [79]: f"{a:8.3s}" # 提取前3个字符,总长度为 8
Out[79]: 'hel '

In [80]: f"{a:3.3s}" # 提取前3个字符,总长度正好为 3
Out[80]: 'hel'

参考文献

  1. https://www.liujiangblog.com/course/python/55

  2. https://blog.csdn.net/sunxb10/article/details/81036693

  3. https://docs.python.org/3/library/string.html#format-string-syntax

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2024 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信