skip to Main Content

My goal is to have python upload files from a set directory to telegram, using telegram-upload, in ascending order. The script that I have cannot seem to upload in order. It uploads the files in random order. I’ve used the sorted function to no avail. Looking at my script you can see some things I’ve tried (commented). I had a setup with sorted that would list the files in order, but when attempting to upload, I couldn’t re-convert the list I had created and sorted back to a string so subprocess could read the arg.

Here’s the script:

import os
import subprocess
import time
#import shutil

Number_Of_Files = 0
#PATH = r'C:UsersmyuserDownloads' 
PATH = '/home/pi/Public/'

for root, subFolder, files in os.walk(PATH):
    for item in files:
        #Number_Of_Files=Number_Of_Files+1
        fileNamePath = os.path.join(root, item)
        #sorted = sorted(fileNamePath)
        #subprocess.run(['telegram-upload', '-f', 'my_channel', str(sorted)])
        subprocess.run(['telegram-upload', '-f', 'my_channel', str(fileNamePath)])
        #os.remove(fileNamePath)
        print(fileNamePath)
        #time.sleep(60)
    #else:
        #print(Number_Of_Files)

2

Answers


  1. sorted does not work in place. The subFolder argument must be sorted in-place if you want to propagate the changes back to os.walk. If you want to use files directly, you need to sort that in-place too. Otherwise, you need to use the sorted version:

    for root, subFolder, files in os.walk(PATH):
        files.sort()  # This sorts in-place
        subFolder.sort() # If you want sorted directories
        for item in files:
            fileNamePath = os.path.join(root, item)
            subprocess.run(['telegram-upload', '-f', 'my_channel', fileNamePath])
    

    If you don’t want to sort the two lists in-place, you could write it like this instead:

    for root, subFolder, files in os.walk(PATH):
        subFolder[:] = sorted(subFolder)  # creates a new list and assigns back to the original
        for item in sorted(files):
            fileNamePath = os.path.join(root, item)
            subprocess.run(['telegram-upload', '-f', 'my_channel', fileNamePath])
    

    It’s pointless to run sorted inside your loop. Since sorted creates an new list every time, and the loop is over the original files list, your call to sorted is having no effect. You generally want to apply sorting before looping over a sequence, as I’ve shown above.

    Login or Signup to reply.
  2. Your whole loop can be greatly simplified by using pathlib and sorted:

    import subprocess
    from pathlib import Path
    
    p=Path('/home/pi/Public/')
    
    for fn in sorted((x for x in p.glob('**/*') if x.is_file())):
        print(fn)
        # subprocess.run(['telegram-upload', '-f', 'my_channel', str(fn)])
    

    The glob('**/*') is a recursive glob equivalent to using os.walk but a little easier to manage. The (x for x in p.glob('**/*') if x.is_file()) is a comprehension that only returns files, not directories and files. The result of that is sorted and away you go…

    Given this folder structure:

    .
    ├── A
    │   ├── b.txt
    │   ├── d.txt
    │   └── y.doc
    └── B
        ├── a.txt
        ├── c.txt
        └── x.doc
    

    sorted((x for x in p.glob('**/*') if x.is_file()) returns files in this order:

    ./A/b.txt
    ./A/d.txt
    ./A/y.doc
    ./B/a.txt
    ./B/c.txt
    ./B/x.doc
    

    If you change the sorted comprehension to sorted((x for x in p.glob('**/*') if x.is_file()), key=lambda x: x.name) then you would sort that same tree only by filename:

    ./B/a.txt
    ./A/b.txt
    ./B/c.txt
    ./A/d.txt
    ./B/x.doc
    ./A/y.doc
    

    Or sort by suffix first then name with sorted((x for x in p.glob('**/*') if x.is_file()), key=lambda x: (x.suffix, x.name)):

    ./B/x.doc
    ./A/y.doc
    ./B/a.txt
    ./A/b.txt
    ./B/c.txt
    ./A/d.txt
    

    With the same method, you can sort by time created, directory name, extension, whatever…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search