How to List Files in a Directory Using Bash: The pi‑web Approach
The pi‑web repository provides two built‑in methods for listing directory contents: a server‑side Node.js utility (listDirectories) and a REST API endpoint (GET /api/files/[…path]), both of which can be invoked from Bash scripts.
The agegr/pi‑web project implements a secure, consistent file‑browsing system that you can tap into from the command line. Whether you need to list files in a directory using Bash for automation, monitoring, or integration tasks, pi‑web offers both direct library access and an HTTP interface. Both approaches share the same core logic in lib/directory-browser.ts, ensuring identical behavior and security policies.
Method 1: Query the HTTP API from Bash
The simplest way to list files in a directory using Bash is to call the built‑in REST endpoint. This requires no Node.js knowledge and works from any shell environment.
The endpoint GET /api/files/[…path]?type=list performs path normalization, symlink resolution, and security filtering before returning a JSON array of directory entries. It automatically excludes common directories like node_modules and .git.
#!/usr/bin/env bash
# List directory contents via pi‑web HTTP API
BASE_URL="http://localhost:30141/api/files"
TARGET="${1:-~}" # First argument or home directory
# URL‑encode the path (strip leading slash)
ENCODED_PATH=$(python3 -c "
import urllib.parse, sys
print(urllib.parse.quote(sys.argv[1].lstrip('/')))
" "$TARGET")
curl -s "${BASE_URL}/${ENCODED_PATH}?type=list" |
jq -r '.entries[] | "\(.isDir?"📁":"📄") \(.name)"'
Save and run: chmod +x list-files.sh && ./list-files.sh /var/log
Security Features of the API Route
The API route at app/api/files/[...path]/route.ts implements several protections:
- Path validation via
resolveDirectory()fromlib/directory-browser.ts - Symlink resolution to prevent directory‑traversal attacks
- Root allow‑list enforcement through
lib/file-access.ts - Ignored pattern filtering (
.git,node_modules,__pycache__, etc.)
Method 2: Invoke the Node.js Library Directly
For server‑side scripts or custom tooling written in TypeScript, call listDirectories() directly. This bypasses HTTP overhead and provides typed results.
import { resolveDirectory, listDirectories } from '@/lib/directory-browser';
// Resolve and validate the path
const dir = await resolveDirectory('~/projects');
// Get typed array of sub‑directories
const subDirs = await listDirectories(dir);
for (const { name, path, isDirectory } of subDirs) {
console.log(`${isDirectory ? 'dir' : 'file'}: ${name} at ${path}`);
}
The listDirectories function in lib/directory-browser.ts returns objects with these properties:
name— entry basenamepath— absolute resolved pathisDirectory— boolean fromresolveDirentIsDirectoryhelper
Core Implementation Files
Understanding these source files helps you extend or debug pi‑web's file‑listing behavior:
| File | Purpose |
|---|---|
lib/directory-browser.ts |
Core utilities: resolveDirectory(), listDirectories(), resolveDirentIsDirectory() |
app/api/files/[...path]/route.ts |
HTTP handler for ?type=list requests; delegates to directory‑browser |
lib/file-access.ts |
Allowed‑roots configuration and path‑validation guards |
hooks/useAgentSession.ts |
Client‑side fetcher for UI file explorer (calls same API) |
Comparison: Which Method to Use
| Approach | Best For | Latency | Dependencies |
|---|---|---|---|
| HTTP API | Bash scripts, external tools, cron jobs | Higher (network) | curl, jq |
Direct listDirectories |
Server‑side TypeScript, custom integrations | Lower (in‑process) | Node.js runtime |
Both methods respect the same ignore patterns and security constraints defined in the source code.
Summary
- Primary method for Bash: Use
curlagainstGET /api/files/[path]?type=listwith URL‑encoded paths - Server‑side alternative: Import
listDirectoriesfromlib/directory-browser.tsfor typed, synchronous‑style enumeration - Security consistency: Both paths invoke
resolveDirectory()andresolveDirentIsDirectory()from the same core module - Data filtering: Automatic exclusion of
node_modules,.git, and configurable junk patterns
Frequently Asked Questions
How do I handle paths with spaces when calling the pi‑web API from Bash?
Quote the argument when invoking your script: ./list-files.sh "/path/with spaces". The Python urllib.parse.quote() in the example properly encodes spaces as %20, ensuring the URL remains valid. The route.ts handler decodes these automatically before validation.
Can I list files recursively instead of just one directory?
The built‑in ?type=list parameter returns a single level. For recursion, either iterate in your Bash script (making successive API calls for each subdirectory) or extend lib/directory-browser.ts to expose a recursive variant. The resolveDirentIsDirectory helper already provides the isDirectory flag needed for traversal logic.
What ports and authentication does the HTTP API require?
By default, pi‑web listens on port 30141 as shown in the examples. Authentication depends on your deployment; the useAgentSession.ts hook suggests session‑based auth for browser clients. For programmatic Bash access, check your lib/file-access.ts configuration—it may allow localhost requests without credentials or require a bearer token in the Authorization header.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →