如何在Python中无引号正确转义Linux路径?
问题
网上关于Unix路径转义的大量回答要么完全错误,要么仅在小范围内适用,未考虑全面情况。我需要将含空格的路径(示例:/home/user/text/Chapter 1-100)转换为单个反斜杠转义空格的格式(即/home/user/text/Chapter\ 1-100),该格式用于生成ffmpeg批量合并音频的concat文件——ffmpeg仅接受这种字面转义格式,不支持带引号的字符串、双反斜杠等写法。
但在Python中尝试多种方法均失败:
shlex.quote仅给字符串添加引号,不符合要求replace(' ', r'\ ')仅能处理空格,且若路径含其他特殊字符(如&、*)会失效os.normpath无法生成所需的转义格式
我编写了一个音频书合并脚本,用natsort排序wav文件并按每5个一组生成concat文本文件,但路径转义问题导致ffmpeg无法识别路径。附脚本代码:
#!/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")
解决方案
方法1:纯Python实现Unix风格路径转义
编写一个转义函数,对所有Unix shell中需要转义的字符(空格、!、#、$、&、'、(、)、*、?、[、]、{、}、\、;、<、>、|、~等)添加单个反斜杠,完全模拟printf '%q'的效果:
def escape_unix_path(path): # 需要转义的特殊字符集合 special_chars = set(r' !#$&\'()*?[]{};><|~') escaped = [] for char in path: if char in special_chars: escaped.append('\\' + char) else: escaped.append(char) return ''.join(escaped)
方法2:调用系统printf命令(依赖Unix环境)
直接调用bash的printf '%q'命令来生成转义路径,效果最准确:
import subprocess def escape_unix_path(path): result = subprocess.run( ['printf', '%q', path], capture_output=True, text=True ) return result.stdout
修改你的脚本
将concat_list函数中的路径处理部分替换为上述转义函数即可:
# 先定义escape_unix_path函数(二选一) def escape_unix_path(path): special_chars = set(r' !#$&\'()*?[]{};><|~') escaped = [] for char in path: if char in special_chars: escaped.append('\\' + char) else: escaped.append(char) return ''.join(escaped) # 修改concat_list函数 def concat_list(): i = 1 total = 0 for arrays in slices: total += len(arrays) name = f"{i}-{total}.txt" i += len(arrays) with open(name, "w") as file: # 改用with语句更安全 for items in arrays: abs_path = os.path.abspath(items) escaped_path = escape_unix_path(abs_path) file.write(f"file {escaped_path}\n") yield name
这样生成的concat文件中,路径会以file /home/user/text/Chapter\ 1-100/file.wav的格式写入,完全符合ffmpeg的要求。
内容的提问来源于stack exchange,提问作者sweet
相关产品推荐
相关产品推荐

