Search code examples
pythonzsh

Reading values from zsh's named directory table from Python


Given a zsh instance that have an entry my_private_dir in it's named directory table and the zsh instance is used to launch a Python script. How could Python read the value of my_private_dir?

For example

$ hash -d my_private_dir=/media/user/encrypted_dir
$ hash -d | grep my_private_dir | sed 's/.*=//'
/media/user/encrypted_dir
$ python
>>> get_zsh_named_dir('my_private_dir') # How could python read it?

I've tried system.os. But it doesn't work, which is expected as it launches a new instance of the shell. Which is a new process and may not have the same named directory table.

# Continuing from the previous code snippet
>>> import os
>>> os.system("echo $SHELL")
/bin/zsh
0
>>> os.system("hash -d | grep my_private_dir | sed 's/.*=//'")
0

So, how could Python read data from the parent zsh's hash table?


Solution

  • Broadly speaking, nothing from your shell is exposed to processes running in it unless exposed specifically, such as via an environment variable. So all you need to do is find a way to expose that table to Python.

    You can do this by reading the data into Python as a script, and running Python with -i to make it become interactive afterwards.

    To feed input to a script while still allowing Python to become interactive, the input can't be given as stdin. But luckily, zsh (and bash) have a way to redirect command output as if it were a file, and Python has an easy way to read input from files.

    Here's the script: it reads equals-separated key/value pairs from files and/or standard input into a dictionary named eqdict.

    """eqdict.py
    
    Translate equals-separated key/value lines into a Python dict.
    """
    try:
        import fileinput
        eqdict = {}
        for line in fileinput.input():
            key, val = line.rstrip().split('=', 1)
            eqdict[key] = val
    finally:
        # Don't pollute the global namespace
        del fileinput, line, key, val
    

    And this is how you can use it:

    zsh% hash -d some_name=./some_dir/
    zsh% python3 -i eqdict.py <(hash -d)
    >>> eqdict['some_name']
    './some_dir/'