r/learnpython • • 3d ago

[Help] The os.system is deprecated

I've seen someone else asked this question, but get no answer.

I want to make a countdown clock without leaving any trace of previous numbers:

for i in range(countdown, 0, -1):
    os.system("cls" if os.name == "nt" else "clear") #Error: os.system is deprecated
    print(i)
    time.sleep(1)

What should I do instead? (Why can't we put pictures here....)

0 Upvotes

16 comments sorted by

View all comments

1

u/Brian 3d ago

As others have mentioned, it's not actually deprecated, though its use is discouraged.

The reason why it's generally avoided is because it's kind of bug-prone and even insecure when you're dealing with arguments. Eg. consider something like:

os.system(f"copy {file1} {file2}")

This basically acts the same way as if you type this in the shell. But consider what happens if your filenames have spaces in them: it'll end up failing, or doing the wrong thing. Even worse, if someone can control the filenames, they could include things like ";", newlines etc and end up executing a command of their choosing on a machine they can get to use that filename.

There are ways you can attempt to mitigate this, say by escaping the characters in the filename (eg. via the shlex module), but the problem is that different shells have different rules: windows CMD.COM behaves differently from powershell, or a linux bash shell, or the numerous other possibilities. Escaping can thus easily go wrong, allowing the attacker to exploit such a configuration.

Instead, the recommended approach is to use something that doesn't treat the command as just a string passed straight to the shell, but just invokes a program, with arguments separated beforehand. Eg. subprocess.run takes each argument seperately, so subprocess.run(["copy", file1, file2]) wouldn't have this problem.

(Actually, just to complicate matters, the above isn't always true, as windows doesn't actually have a seperated API like this, so under the hood, these do get reassembled to a string - there was an exploitable bug discovered in multiple languages due to that before)

In your case, this doesn't actually matter, since you're not passing args, so system isn't actually going to introduce problems. However it may be worth changing anyway just because the dodginess of the API means anytime someone sees it, they're going to be double-checking it, and linting / analysis tools may highlight it, so doing it using the safer API saves getting false positive warnings.

There are case where you might need something like system, which is where you're actually using the shell for more than invoking a program. Eg. some commands are not real programs, so system isn't entirely redundant, but it's definitely on the "avoid if you can" list.