Aakash Singh
Aakash Singh

Reputation: 479

columns using string formatting python

I'm trying to replicate the ls function in bash with columns like this: enter image description here

I tried .format to add padding to the file names but im still not getting the desired output the last column overflows everytime.

def show_dirs():
    filelist = os.listdir(os.getcwd())
    filelist.sort()

    scr_width = int(os.popen("stty size", "r").read().split()[1])
    max_len = -1
    for file_len in filelist:
        if len(file_len) > max_len and scr_width > max_len*2:
            max_len = len(file_len)
            mlen = max_len #+ 1

    print(color["cyan"] + "Files in the current directory")

    if scr_width < mlen:
        mlen = scr_width

    line = ""
    for count, _file in enumerate(filelist):
        if os.path.isdir(_file):
            _file = _file + os.sep
            st = "[{0:>2}] {1:<{mlen}}".format(str(count + 1), _file, mlen=mlen)
            if len(line) % scr_width > len(st):
                line = line + color["green"] + st
            else:
                line = line + "\n" + color["green"] + st
        else:
            st = "[{0:>2}] {1:<{mlen}}".format(str(count + 1), _file, mlen=mlen)
            if len(line) % scr_width > len(st):
                line = line + color["cyan"] + st
            else:
                line = line + "\n" + color["cyan"] + st
    print(line)

I get this output:

enter image description here

Upvotes: 1

Views: 148

Answers (2)

Aakash Singh
Aakash Singh

Reputation: 479

I improved my algorithm and now it works fine when the terminal size is fixed

color = {
    "red": "\033[31m",
    "green": "\033[32m",
    "orange": "\033[33m",
    "purple": "\033[35m",
    "cyan": "\033[36m",
    "yellow": "\033[93m",
}


def show_dirs(path=os.getcwd()):
    print(color["cyan"] + "Files in the current directory")
    filelist = os.listdir(path)
    filelist.sort()

    #index padding
    ind = len(filelist)
    if ind >= 1000:
        ind = 4
    elif ind >= 100:
        ind = 3
    elif ind >= 10:
        ind = 2
    else:
        ind = 1

    scr_width = int(os.get_terminal_size()[0]) #terminal width
    mlen = max(len(word) for word in filelist) + 1 #column width
    cols = scr_width // mlen #possible columns

    if scr_width < mlen:
        mlen = scr_width

    line = ""
    lst = []
    for count, _file in enumerate(filelist, start=1):
        if os.path.isdir(_file):
            _file = _file + os.sep
            st = "[{0:>{ind}}] {1:<{mlen}}".format(
                str(count), _file, mlen=mlen, ind=ind
            )
            if scr_width - ((len(line) - cols * 5) % scr_width) > len(st): # - cols * 5 to compensate the length of color codes
                line = line + color["cyan"] + st
            else:
                lst.append(line)
                line = color["cyan"] + st

        else:
            st = "[{0:>{ind}}] {1:<{mlen}}".format(
                str(count), _file, mlen=mlen, ind=ind
            )
            if scr_width - ((len(line) - cols * 5) % scr_width) > len(st): # - cols * 5 to compensate the length of color codes
                line = line + color["green"] + st
            else:
                lst.append(line)
                line = color["green"] + st
    if lst == []:
        lst.append(line)
    print("\n".join(lst))

output: enter image description here

Upvotes: 0

Amitai Irron
Amitai Irron

Reputation: 2055

So, it turns out that column output of a list of arbitrary strings is done by the Linux column command. One option is to assume the existence of this command on your system (it exists in my environment) and to use it as an out-of-process output filter.

Another option is to dig deeper into the source code of columns, and reimplement it in Python, possibly as a generator function. I found a pointer in GitHub. Note that this is over 800 lines of C code.

You can also create Python binding for the core functionality of that C code, which would yield a better performing code. Sadly I do not have experience with integrating C code into Python.

If you decide to opt for the second alternative, you can post your implementation as an answer here.

Upvotes: 1

Related Questions