如何在 Python3 代码中实现 bash oneliner?

How to implement bash oneliner into Python3 code?

提问人:Durable Metal 提问时间:7/21/2022 更新时间:7/21/2022 访问量:103

问:

我有 bash oneliner,但我不能使用它实现到 python3 中并在 Rhel 服务器上运行它。

 for i in {5000..10000}; do if grep "no such user" <(id $i 2>&1 ) > /dev/null; then echo $i ; break ; fi; done

我已经试过了

print(subprocess.check_output('bash -c "for i in {5000..10000}; do if grep "no such user" <(id $i 2>&1 ) > /dev/null; then echo $i ; break ; fi ; done" ', shell=True).decode())

和这个

p1 = Popen(["for i in {5000..10000}; do if grep "no such user" <(id $i 2>&1 ) > /dev/null; then echo $i ; break ; fi; done"], stdout=PIPE)

print p1.communicate()

也试过这个

command = '''
    for i in {5000..10000}
do
    if grep "no such user" <(id $i 2>&1 ) > /dev/null
    then echo $i
    break
    fi
done
'''
        uid = subprocess.run(command, capture_output=True, shell=True)

我总是得到 SyntaxError:语法无效 或

CompletedProcess(args='\n    for i in {5000..10000}\ndo\n    if grep "no such user" <(id $i 2>&1 ) > /dev/null\n    then echo $i\n    break\n    fi\ndone\n', returncode=1, stdout=b'', stderr=b'/bin/sh: -c: line 3: syntax error near unexpected token `(\'\n/bin/sh: -c: line 3: `    if grep "no such user" <(id $i 2>&1 ) > /dev/null\'\n')

你能帮帮我,说说我做错了什么吗?我已经失去了无数小时的调试时间,并希望得到您的帮助。

python-3.x bash 子进程 语法错误 popen

评论

1赞 Charles Duffy 7/21/2022
请注意,这是一个非常低效 shell 脚本。没有充分的理由运行,而且您可以要求操作系统直接检索条目数千次。idgreppasswd
1赞 Charles Duffy 7/21/2022
该版本预计不会起作用,因为它使用 ,而不是 ,并且您的代码具有一堆特定于 bash 的逻辑。shell=True/bin/shbash
1赞 Charles Duffy 7/21/2022
使用双引号将包含双引号的 shell 脚本括起来的版本也不应起作用,因为这不是有效的 Python 字符串语法(除非您在字符串中使用反斜杠来转义内部引号)。
1赞 Charles Duffy 7/21/2022
第一个版本,你有一个脚本开始运行一个脚本......光是想想它将如何解析就让我头疼。别这样。shbashbash
1赞 Charles Duffy 7/24/2022
...因此,一种更有效的纯 shell 方法来完成您的目标(仍然使用仅 bash 语法)可能是 .我希望这至少比您当前的代码快一个数量级。{1..2}printf '%s\n' {50000..10000} | xargs -d $'\n' getent passwd | cut -d: -f3

答:

2赞 Barmar 7/21/2022 #1

在 Python 中执行循环和输出检查。

for i in range(5000, 10001):
    output = subprocess.check_output(['id', str(i)], stderr=subprocess.DEVNULL, encoding='utf-8')
    if 'no such user' in output:
        print(i)
        break

Python 还有一个用于搜索用户数据库的 pwd 模块。

from pwd import getpwuid

for i in range(5000, 10001):
    try:
        getpwuid(i)
    except KeyError:
        print(i)
        break

评论

1赞 Charles Duffy 7/21/2022
Re:执行该操作的库函数 -- pypi.org/project/getent;也可以使用 FFI 直接调用 libc 函数,而无需做太多工作。同样相关的是用于列出用户和组的 python 脚本id $i
2赞 Barmar 7/21/2022
@CharlesDuffy 这些看起来不太容易使用(文档非常备用,我不知道它是否需要 uid 参数),但我找到了内置模块。getentpwd
1赞 Charles Duffy 7/24/2022
是的,FWIW 可以用 uid 代替名称; 正确返回 的条目,并且(在我有条目的 Linux 盒子上)首先返回 root 的条目,然后返回 dbus 用户的条目,这是我的发行版分配给 UID 4 的帐户,同时忽略那些没有分配帐户的 UID——因此可以在每个命令行上传递多个 UID,而不是对每个 UID 进行单独的调用, 允许 xargs 类型折叠。getentgetent passwd 0rootgetent passswd 0 1 2 3 4
2赞 Charles Duffy 7/21/2022 #2

虽然更好的答案是在 Python 中完成这一切(这将大大提高效率),但你没有理由不能用子进程来做到这一点。

使用三引号的原始字符串,以便您的 bash 代码存储在 Python 字符串中,而无需对其进行任何修改:

#!/usr/bin/env python
import subprocess

bash_code=r'''
for i in {5000..10000}; do if grep "no such user" <(id "$i" 2>&1 ) > /dev/null; then echo "$i" ; break ; fi; done
'''
p = subprocess.run(['bash', '-c', bash_code], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(f"stdout is: {f.stdout}")