需求:将指定Shell脚本转Python代码或嵌入Python并支持路径变量替换
Great question! Let's start by recapting what your original shell script does: it outputs a formatted table showing the total size, number of files/subdirectories, and name of each subdirectory in a given path, sorted by the number of files. Below are two approaches to convert this to Python using a variable path (e.g., x = "/usr/bin").
Approach 1: Embed the Shell Script in Python
If you want to reuse your existing shell logic without rewriting everything, you can use Python's subprocess module to run the command dynamically with your path variable. This is ideal if you're comfortable with the original shell behavior and want a quick port.
import subprocess # Define your target path variable x = "/usr/bin" # Calculate the field number for extracting subdirectory names (adjusts for path depth) path_depth = x.count('/') + 2 # Build the shell command with the dynamic path shell_cmd = f'''echo -e "Size\tFiles\tDirectory"; paste <(du -sh {x}/*/ | sort -k2 | cut -f1) <(find {x}/*/ | cut -d/ -f{path_depth} | uniq -c | sort -k2 | awk '{{print ($1-1)"\\t"$2}}') | sort -nk2''' # Run the command and capture output (handle errors gracefully) try: result = subprocess.run(shell_cmd, shell=True, capture_output=True, text=True, check=True) print(result.stdout) except subprocess.CalledProcessError as e: print(f"Error running command: {e.stderr}")
Key Notes:
- The
path_depthcalculation ensures we correctly extract subdirectory names regardless of how nested your target path is (e.g.,/usr/binvs/home/user/documents). - Wrap your path in quotes (
"{x}") if it contains spaces or special characters (like*or?) to avoid shell parsing issues. - This relies on Unix-like shell tools (
du,find,paste, etc.) being available on your system.
Approach 2: Pure Python Implementation
For cross-platform compatibility and easier long-term modification, rewrite the logic entirely in Python. This eliminates dependencies on external shell tools and gives you more control over edge cases.
import os def format_size(bytes_size): """Convert raw bytes to human-readable format (KB, MB, GB, etc.)""" for unit in ['B', 'KB', 'MB', 'GB', 'TB']: if bytes_size < 1024: return f"{bytes_size:.1f}{unit}" bytes_size /= 1024 return f"{bytes_size:.1f}PB" def get_directory_stats(dir_path): """Calculate total size and number of files/subdirs for a given directory""" total_size = 0 total_entries = 0 try: for root, dirs, files in os.walk(dir_path): # Add size of all files (skip unreadable files) for file in files: file_path = os.path.join(root, file) try: total_size += os.path.getsize(file_path) except OSError: continue # Count all directories and files in the current tree level total_entries += len(dirs) + len(files) except OSError: # Skip directories we can't access (e.g., permission denied) return None, None return format_size(total_size), total_entries # Define your target path variable x = "/usr/bin" # Collect stats for all subdirectories directory_stats = [] for entry in os.scandir(x): if entry.is_dir(follow_symlinks=False): size, count = get_directory_stats(entry.path) if size and count: directory_stats.append((count, size, entry.name)) # Sort by number of entries (numerical order) directory_stats.sort() # Print formatted table print(f"{'Size':<8} {'Files':<6} Directory") for count, size, name in directory_stats: print(f"{size:<8} {count:<6} {name}")
Key Notes:
- The
format_sizefunction replicates the human-readable output ofdu -sh. - We handle permission errors by skipping unreadable files/directories, making the script more robust.
os.scandiris more efficient thanos.listdirfor checking directory entries.- By default, this doesn't follow symbolic links (matching
du's default behavior). To follow symlinks, changefollow_symlinks=FalsetoTrueinentry.is_dir().
内容的提问来源于stack exchange,提问作者robinder singh

