Skip to content
shellhacks.dev
Python

How to Use Python One Liners From the Shell With Examples

Use python -c one liners from the shell: format JSON, encode and decode, do math, generate passwords and serve files, with quoting tips for Bash.

7 min read
Summarize in AI
Illustration for "How to Use Python One Liners From the Shell With Examples": a terminal session, a request travelling to a server, text before and after a transform.

Python is on most machines, and for a quick calculation, a format conversion or a throwaway server it replaces awk, bc and a handful of utilities with one tool that you already know. The risk is writing something clever that nobody can read later. This post collects one-liners that earn their place and shows how to quote them. It complements the REPL and ipython, and assumes Python 3.13.

Calculations and conversions

Shell
python3 -c 'print(2**64)'
python3 -c 'import math; print(math.ceil(1000/48))'
python3 -c 'import datetime; print(datetime.datetime.fromtimestamp(1700000000, datetime.timezone.utc))'
python3 -c 'import sys; print(sum(float(x) for x in sys.stdin))' < numbers.txt
Text
18446744073709551616
21
2023-11-14 22:13:20+00:00
1742.5

Python handles big integers without overflow and has exact arithmetic for integers, which makes it better than bc for many tasks. The last example reads numbers from a file through standard input and sums them, an alternative to awk that you can extend easily.

JSON, encoding and identifiers

Shell
echo '{"b":1,"a":[1,2]}' | python3 -m json.tool
echo '{"b":1,"a":2}' | python3 -m json.tool --sort-keys --compact
python3 -c 'import base64,sys; print(base64.b64encode(sys.argv[1].encode()).decode())' 'hello'
python3 -c 'import uuid; print(uuid.uuid4())'
python3 -c 'import secrets; print(secrets.token_urlsafe(24))'
python3 -c 'import urllib.parse as u; print(u.quote("a b&c/d", safe=""))'
Text
{
    "b": 1,
    "a": [
        1,
        2
    ]
}
{"a":2,"b":1}
aGVsbG8=
b6c1e2f4-9a37-4f0d-8b52-1c2d3e4f5a6b
Xk3pQ9vL2mRn7TfYw5aBcDeF0gH8iJ1k
a%20b%26c%2Fd

Values like the random string and UUID are examples, not fixed outputs. json.tool is installed with Python, so no jq is needed for pretty-printing, though it cannot filter. secrets uses the operating system's secure source and is the right module for tokens, unlike random. See also JSON queries in Python.

Serve a directory

Shell
python3 -m http.server 8000 --bind 127.0.0.1
python3 -m http.server 8000 --directory /srv/site

This serves files over HTTP for quick sharing or testing a static site. It has no authentication, no TLS and no protection against directory listing, so bind to localhost or use it only on a trusted network, and stop it when finished. It is for convenience, not production.

Quoting and longer programs

Shell
python3 -c "print('single quotes inside')"
python3 - <<'EOF'
import json, sys
data = json.load(open("config.json"))
for key, value in sorted(data.items()):
    print(f"{key}={value}")
EOF

Use single quotes around the program so Bash does not expand $, backticks or !, and double quotes inside it for strings. When both kinds of quote are needed, or the code has more than one statement, switch to a quoted here-document, where the delimiter in quotes disables expansion entirely. The same trap with variables is explained in passing variables into awk. Never build the code string from untrusted input; pass data as arguments and read sys.argv.

When a one-liner is the right size

The -c option runs a string as a program, which makes Python useful where a shell would be clumsy: arithmetic with big integers, date calculations, URL encoding, JSON formatting and hashing. A good one-liner answers one question, produces output the shell can use and fits in a single readable line. When it needs a second line, a loop with a body or a comment to explain it, it has outgrown the form. Put it in a file, name it and give it a shebang. A one-liner that nobody can read in a month is a liability in a script, even if it was clever when written.

Start-up cost is a consideration. Launching the interpreter takes tens of milliseconds, which is nothing for a single call and adds up in a loop of ten thousand. Do the work in one Python process over all the input, not one process per item.

Reading from pipes

Python reads standard input like any file, so it slots into pipelines. python3 -c 'import sys; print(sum(int(l) for l in sys.stdin))' sums numbers, and python3 -m json.tool pretty-prints JSON from a pipe without any code of your own. The module runner flag -m gives access to a number of standard library tools: http.server to serve a directory, json.tool to format, calendar to print a calendar, base64 to encode, zipfile to list or create archives and timeit to measure a snippet. These are easy to remember and need no quoting.

For line-oriented work, the -n and -p switches that Perl users expect do not exist in Python, so loop over sys.stdin explicitly. The structure is short and clear enough.

Quoting and the shell

The shell and Python both interpret quotes, and mixing them is the main source of one-liner errors. Wrap the program in single quotes and use double quotes inside it, which avoids shell expansion of dollar signs and backticks. If the program needs a single quote, close the shell quote, add an escaped quote and reopen, or switch to a heredoc: python3 - <<'EOF' followed by the code and a terminating line passes the program on standard input with no quoting at all. The quoted delimiter prevents the shell from expanding anything inside. Heredocs also allow multi-line programs and keep the shell script readable. Pass values in as arguments and read them from sys.argv, not by interpolating shell variables into the code, which invites the same injection problems as in awk.

Remember that indentation matters: a multi-statement body after a colon needs consistent indentation, and a one-liner cannot contain a compound statement after a semicolon.

Reproducibility and the interpreter version

The interpreter found as python3 differs between systems, and so does the standard library. Features like the walrus operator, structural pattern matching and newer module functions need recent versions. On an old server, a one-liner using such a feature fails with a syntax error that looks mysterious. State the minimum version where a script depends on it, and check with python3 --version at the top of the script. Avoid reaching for third-party packages in one-liners: they may not be installed, and a missing import in an unattended job is a silent failure waiting for a night shift. Stay within the standard library, handle bad input with a clear message and a non-zero exit status, and a one-liner becomes a dependable part of a script instead of a trap.

Typical tasks show where the form earns its place. A UUID for a test fixture comes from python3 -c 'import uuid; print(uuid.uuid4())', a URL-encoded string from urllib.parse.quote, a timestamp converted from epoch seconds with datetime.fromtimestamp, and a quick hash of a file with hashlib. Each is a single expression with a single printed result, which is the shape that suits the command line. When you find yourself chaining three of them with semicolons, write a script instead, because the first change request will need the structure.

Document any one-liner you commit to a repository with a comment above it that states the input, the output and the Python version it assumes. The comment costs a line and saves the next reader from reverse engineering a dense expression. Add it to the project's test suite if the script depends on its exact output, so a change of interpreter or library version cannot alter it unnoticed.

Keep the interactive interpreter in mind as the sibling of the one-liner. For exploring an unfamiliar library, a session in IPython, with tab completion and help, beats repeated -c invocations, and the history of the session can be saved into a script once it works.

Where this does not apply

A one-liner that you run twice should become a script, and one that you share should have a name, a docstring and a test. Startup time of Python is tens of milliseconds, which matters in a loop of thousands of calls; do the loop inside one Python process instead. On systems where only python or only python3 exists, a script needs to name the one it wants. See the Python command line reference and the json.tool documentation.

Share

Keep reading

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.