diff --git a/main.go b/main.go
index 481a33f..d8fa911 100644
--- a/main.go
+++ b/main.go
@@ -84,6 +84,7 @@ func main() {
 			processor.LocomoOutputPriceSet = cmd.PersistentFlags().Changed("locomo-output-price")
 			processor.LocomoTPSSet = cmd.PersistentFlags().Changed("locomo-tps")
 			processor.LocomoCyclesSet = cmd.PersistentFlags().Changed("locomo-cycles")
+			processor.SortBySet = cmd.PersistentFlags().Changed("sort")
 
 			processor.Process()
 		},
@@ -442,6 +443,30 @@ func main() {
 		"",
 		"have multiple format output overriding --format [e.g. tabular:stdout,csv:file.csv,json:file.json]",
 	)
+	flags.BoolVar(
+		&processor.BoundedMemory,
+		"bounded-memory",
+		false,
+		"spill per-file results to disk to bound memory usage for --format-multi (requires --bounded-memory-dir and --bounded-memory-max-in-memory-files)",
+	)
+	flags.StringVar(
+		&processor.BoundedMemoryDir,
+		"bounded-memory-dir",
+		"",
+		"directory per-file results are spilled into, created if missing (required when --bounded-memory is enabled)",
+	)
+	flags.IntVar(
+		&processor.BoundedMemoryMaxInMemoryFiles,
+		"bounded-memory-max-in-memory-files",
+		0,
+		"maximum number of per-file results held in memory at once, must be > 0 (required when --bounded-memory is enabled)",
+	)
+	flags.BoolVar(
+		&processor.BoundedMemoryStats,
+		"bounded-memory-stats",
+		false,
+		"print bounded memory statistics such as spills and peak in memory files to stderr",
+	)
 	flags.StringVar(
 		&processor.SQLProject,
 		"sql-project",
diff --git a/processor/processor.go b/processor/processor.go
index ec6ab6a..120ecac 100644
--- a/processor/processor.go
+++ b/processor/processor.go
@@ -136,6 +136,21 @@ var Format = ""
 // FormatMulti is a rule for defining multiple output formats
 var FormatMulti = ""
 
+// BoundedMemory enables bounded memory mode which spills per file results to disk to limit memory usage
+var BoundedMemory = false
+
+// BoundedMemoryDir is the directory used to spill per file results into when BoundedMemory is enabled
+var BoundedMemoryDir = ""
+
+// BoundedMemoryMaxInMemoryFiles is the maximum number of per file results held in memory at once when BoundedMemory is enabled
+var BoundedMemoryMaxInMemoryFiles = 0
+
+// BoundedMemoryStats enables printing bounded memory statistics such as spills and peak in memory files to stderr
+var BoundedMemoryStats = false
+
+// SortBySet indicates whether a sort order was explicitly requested on the command line
+var SortBySet = false
+
 // SQLProject is used to store the name for the SQL insert formats but is optional
 var SQLProject = ""
 
@@ -582,6 +597,34 @@ func Process() {
 	ProcessConstants()
 	processFlags()
 
+	if BoundedMemory {
+		if strings.TrimSpace(BoundedMemoryDir) == "" {
+			_, _ = fmt.Fprintf(os.Stderr, "--bounded-memory-dir must be supplied when --bounded-memory is enabled\n")
+			os.Exit(1)
+		}
+
+		if BoundedMemoryMaxInMemoryFiles <= 0 {
+			_, _ = fmt.Fprintf(os.Stderr, "--bounded-memory-max-in-memory-files must be greater than 0 when --bounded-memory is enabled\n")
+			os.Exit(1)
+		}
+
+		// The spill directory may not exist yet so create it, and ensure it is
+		// excluded from counting should it live inside one of the paths being scanned
+		if err := os.MkdirAll(BoundedMemoryDir, 0755); err != nil {
+			_, _ = fmt.Fprintf(os.Stderr, "unable to create bounded memory spill directory %s: %s\n", BoundedMemoryDir, err)
+			os.Exit(1)
+		}
+
+		spillDir := filepath.Clean(BoundedMemoryDir)
+		PathDenyList = append(PathDenyList, spillDir)
+		if absSpillDir, err := filepath.Abs(spillDir); err == nil && absSpillDir != spillDir {
+			PathDenyList = append(PathDenyList, absSpillDir)
+			if relSpillDir, err := filepath.Rel(".", absSpillDir); err == nil && relSpillDir != spillDir {
+				PathDenyList = append(PathDenyList, relSpillDir)
+			}
+		}
+	}
+
 	// Clean up any invalid arguments before setting everything up
 	if len(DirFilePaths) == 0 {
 		DirFilePaths = append(DirFilePaths, ".")
