Escaping a Linux path in Python without quotes, the correct way

Question:

I’m seeing a large amount of answers on here to this question that are blatantly incorrect or are partially correct but don’t account for any variances outside of a small scope.

here is my path:

"/home/user/text/Chapter 1-100"

In bash/zsh I can hit tab and have this path properly escaped, or I can use:

printf '%q' "/home/user/text/Chapter 1-100"

returns:

/home/user/text/Chapter 1-100

which is exactly what I need and I cannot compromise on this whatsoever, it must be escaped this way to be a compatible path for concatenating a few hundred files with ffmpeg in batches of 5. Ffmpeg only accepts this path style, it does NOT accept quoted strings, single quoted strings, double backslashes ‘\’ or 4 backslashes.

The problem is that I am unable to recreate this using python. Shlex.quote just quotes the string. Using:

replace(' ', r' ') or (' ', ' ') or (' ', '\ ') or (' ', '\') or (' ', r'\ ')

os.normpath(path) also isn’t giving me a valid path. Most of these either leave the spaces untouched, or add TWO backslashes, I am baffled as to why it wont just allow me to have a single backslash. No matter what I try, I either get a space or I get two backslashes.

I need the literal Unix accepted, escaped path. Does anyone know how I can achieve this?

More context:
I wrote a script that uses natsort to sort wav files in order (because they are audio books and need to be in order) then iterates over the list of the few hundred wav files and splits them into groups of 5 each and then appends that group to a file in a format that ffmpeg accepts ex:

file path/to/the/file/Chapter 200/file.wav

So in the end I am left with Numbered text files 1-5.txt – 6-10.txt etc.. that can easily be used with ffmpeg to concat these files for later processing into mp4 files.

This issue pertains to the path needing to be properly escaped. ffmpeg isnt accepting anything besides the UNIX standard path expansion " " instead of " "

As asked here is the code:

#!/bin/env python3
import os
import sys
import glob
import re
import ffmpeg
from rich import print
from natsort import natsorted
import shutil
import shlex


def natsorter(list, path):
    files = glob.glob(f"{path}/*.wav")
    files = natsorted(files)
    return files


# def sorted_alphanumeric(data):
# convert = lambda text: int(text) if text.isdigit() else text.lower()
# alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
# return sorted(data, key=alphanum_key)

# this is where I start sorting wav files
# add fils to a list, sort list and create a new list
# that are lists of 5 audio files ie 1-5, 6-10 etc...
newlist = []
path = os.getcwd()
files = glob.glob(f"{path}/*.wav")
# send list of files and path of files to sort them numerically in order
# this is important for an audiobook
newlist = natsorter(files, path)

# create empty slices array
slices = []


# function to slice an array into chunks of {n}
def divide_chunks(l, n):
    # Looping until length 1
    # start at 0 (first), to the full length, n is the step
    for i in range(0, len(l), n):
        # yield is like return it creates a list object and appends stuff to it
        yield l[i : i + n]
        slices.append(l[i : i + n])


# n is the number of files to slice together in ffmpeg file
# ie "file path/to/file.wav"
n = 5
x = list(divide_chunks(newlist, n))
print("--------------------")
print("SLICES YIELD: ")
print(x)
print("--------------------")


# create concat text files for ffmpeg
def concat_list():
    # files_concat = []
    i = 1
    total = 0
    for arrays in slices:
        total += len(arrays)
        name = f"{i}-{total}.txt"
        i += len(arrays)
        file = open(name, "w")
        for items in arrays:
            items = os.path.abspath(items).replace(" ", r" ")
            file.write("file " + items + "n")
        file.close()
        # files_concat.append(name) -> switched to yield :) i learned a thing
        yield name


catfiles = list(concat_list())
# catfiles = []
print(catfiles)
for x in catfiles:
    ffmpeg.input(x, f="concat", safe=0).output(
        f"{x}.wav", codec="copy"
    ).overwrite_output().run()
    catfiles.append(f"{x}.wav")


Asked By: sweet

||

Answers:

You don’t need to write escape sequences. The documentation of concat says:

file path
Path to a file to read; special characters and spaces must be escaped with backslash or single quotes.

So just put single quotes around the filename.

        for items in arrays:
            items = os.path.abspath(items)
            file.write(f"file '{items}'n")
Answered By: Barmar
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.