Build a spreadsheet report of a folder
What it does. The script lists the files in a folder, adds their size, date and a field, puts a totals row at the bottom, and writes the lot to an Excel file in a Reports folder. Run it by hand, or from a schedule every Monday.
What you need.
- The MaSH feature.
- A source folder and a Reports folder, on shares the script’s owner can use.
- Optionally a schedule: Settings > MaSH > Schedule, Weekly, with None: no input files. See Schedules.
- Script settings:
SHARE,FOLDERandREPORTS(folder for the output).
The rows, on their own
Section titled “The rows, on their own”The report is a list of rows, the first being the headings. This block builds the rows from a list of simple records, so you can test it without a share:
block reportRows items set rows to [["Name", "Size (KB)", "Modified", "Status"]] set totalBytes to 0
each items as item set kb to mash.math.round(item["size"] / 1024, 1) rows[] = [item["name"], kb, item["modified"], item["status"] ?? ""] set totalBytes to totalBytes + item["size"] end
rows[] = ["Total", mash.math.round(totalBytes / 1024, 1), "", count(items) ~ " files"] return rowsend
set sample to [ {"name": "a.pdf", "size": 2048, "modified": "2026-11-01", "status": "Approved"}, {"name": "b.pdf", "size": 1536, "modified": "2026-11-02"}]
each reportRows(sample) as row printline mash.collect(row).join(" | ")endName | Size (KB) | Modified | Statusa.pdf | 2 | 2026-11-01 | Approvedb.pdf | 1.5 | 2026-11-02 |Total | 3.5 | | 2 filesblock reportRows(items) { rows = [["Name", "Size (KB)", "Modified", "Status"]] totalBytes = 0
each (items) as item { kb = mash.math.round(item["size"] / 1024, 1) rows[] = [item["name"], kb, item["modified"], item["status"] ?? ""] totalBytes = totalBytes + item["size"] }
rows[] = ["Total", mash.math.round(totalBytes / 1024, 1), "", count(items) ~ " files"] return rows}
sample = [ {"name": "a.pdf", "size": 2048, "modified": "2026-11-01", "status": "Approved"}, {"name": "b.pdf", "size": 1536, "modified": "2026-11-02"}]
each (reportRows(sample)) as row { printline(mash.collect(row).join(" | "))}Name | Size (KB) | Modified | Statusa.pdf | 2 | 2026-11-01 | Approvedb.pdf | 1.5 | 2026-11-02 |Total | 3.5 | | 2 filesThe script
Section titled “The script”set errorblock to "onError"block onError printline "Error on line {{mash.error.line}}: {{mash.error.message}}"end
set shareName to mash.config.get("SHARE")set folderPath to mash.config.get("FOLDER") ?? ""set reportsPath to mash.config.get("REPORTS") ?? "Reports"
if not shareName fail "SHARE is not set on this automation."end
set folder to mash.files.get(shareName, folderPath)if not folder.is_dir fail "{{folderPath}} is not a folder."end
set items to []each folder.contents as item if not item.is_dir items[] = { "name": item.name, "size": item.size, "modified": item.modified.format("Y-m-d"), "status": item.fields["Status"] ?? "" } endend
if count(items) is 0 printline "No files in {{folderPath}}. No report written." returnend
set rows to [["Name", "Size (KB)", "Modified", "Status"]]set totalBytes to 0each items as item rows[] = [item["name"], mash.math.round(item["size"] / 1024, 1), item["modified"], item["status"]] set totalBytes to totalBytes + item["size"]endrows[] = ["Total", mash.math.round(totalBytes / 1024, 1), "", count(items) ~ " files"]
set book to mash.xlsx.create(rows, "Folder report")
set reports to mash.files.get(shareName, reportsPath)set fileName to "report-" ~ mash.date.now().format("Y-m-d") ~ ".xlsx"reports.write(fileName, book)
printline "Wrote {{fileName}} with {{count(items)}} files."errorblock = "onError"block onError() { printline("Error on line {{mash.error.line}}: {{mash.error.message}}")}
shareName = mash.config.get("SHARE")folderPath = mash.config.get("FOLDER") ?? ""reportsPath = mash.config.get("REPORTS") ?? "Reports"
if (not shareName) { fail "SHARE is not set on this automation."}
folder = mash.files.get(shareName, folderPath)if (not folder.is_dir) { fail "{{folderPath}} is not a folder."}
items = []each (folder.contents) as item { if (not item.is_dir) { items[] = { "name": item.name, "size": item.size, "modified": item.modified.format("Y-m-d"), "status": item.fields["Status"] ?? "" } }}
if (count(items) == 0) { printline("No files in {{folderPath}}. No report written.") return}
rows = [["Name", "Size (KB)", "Modified", "Status"]]totalBytes = 0each (items) as item { rows[] = [item["name"], mash.math.round(item["size"] / 1024, 1), item["modified"], item["status"]] totalBytes = totalBytes + item["size"]}rows[] = ["Total", mash.math.round(totalBytes / 1024, 1), "", count(items) ~ " files"]
book = mash.xlsx.create(rows, "Folder report")
reports = mash.files.get(shareName, reportsPath)fileName = "report-" ~ mash.date.now().format("Y-m-d") ~ ".xlsx"reports.write(fileName, book)
printline("Wrote {{fileName}} with {{count(items)}} files.")How it works
Section titled “How it works”folder.contentsis the list of what’s directly inside. The script keeps only files, not folders. To include subfolders, callcontentsagain on each folder in a loop, or usemash.files.search.- Plain records first. The script turns each file into a small dictionary and builds rows from those. The row-building block is then the same one you tested above.
item.fields["Status"] ?? ""reads a field and falls back to empty, so a file with no value doesn’t stop the run.mash.xlsx.create(rows, name)makes the workbook in memory.reports.write(fileName, book)saves it. A new name each day keeps old reports.- A run has a time limit. A folder with thousands of files can take a while. See Limits. For big folders, report on one subfolder per run.
Variations
Section titled “Variations”- A CSV instead. Write rows with
mash.csv.writer()andinsertOnefor each. See Log each document’s fields to a CSV. - Email it. Send the written file as an attachment:
mash.mail.send("boss@example.com", "Weekly report", "Attached.", [written]), wherewritten = reports.write(fileName, book). - Several shares. Loop over a list of share names from a setting and add a Share column.
- Filter. Skip files that aren’t PDFs:
if item.extension == "pdf". - Sums by field.
mash.collect(items).sum("size")totals one key. Group rows with the collection methods on the Collection page.