Skip to content

Rename files from their fields

What it does. When a document’s fields are filled in, the script renames it to <Customer> - <Order No> - <Type>.<ext>. Odd characters are cleaned out, empty parts are skipped, and a file that already has the right name is left alone.

What you need.

  • The MaSH feature.
  • Fields on the share, here Customer, Order No and Type.
  • Triggers: Fields Changed (a person or script edits the fields), and Captur Extracted if Captur fills them. Filter to the share’s path and, if you like, to an extension. See Events. Don’t rely on Indexed: the fields are often empty then.
  • A script setting PATTERN_FIELDS: the field names in order, separated by commas. The default is Customer,Order No,Type.

The part worth testing is the name. This block builds it, and you can run it without a share:

block cleanPart raw
set part to mash.str(raw ?? "").trim().toString()
set part to mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString()
return mash.str(part).trim().toString()
end
block buildName values extension
set parts to []
each values as v
set clean to cleanPart(v)
if clean is not ""
parts[] = clean
end
end
if count(parts) is 0
return ""
end
return mash.collect(parts).join(" - ") ~ "." ~ extension
end
printline buildName(["Acme Ltd", "SO-20417", "Despatch Note"], "pdf")
printline buildName(["A/B Traders", null, "Invoice"], "pdf")
printline buildName([null, "", " "], "pdf")
Acme Ltd - SO-20417 - Despatch Note.pdf
A-B Traders - Invoice.pdf

The last call returns an empty name, so it prints an empty line. The script must treat an empty name as “do nothing”.

set errorblock to "onError"
block onError
printline "Error on line {{mash.error.line}}: {{mash.error.message}}"
end
block cleanPart raw
set part to mash.str(raw ?? "").trim().toString()
set part to mash.str(part).replaceMatches('#[\\/:*?"<>|]#', "-").toString()
return mash.str(part).trim().toString()
end
set doc to mash.event.file
if not doc
return
end
if doc.is_dir
return
end
set fieldList to mash.config.get("PATTERN_FIELDS") ?? "Customer,Order No,Type"
set parts to []
each fieldList.split(",") as fieldName
set fieldName to mash.str(fieldName).trim().toString()
set value to ""
if doc.fields.has(fieldName)
set value to cleanPart(doc.fields[fieldName] ?? "")
end
if value is not ""
parts[] = value
end
end
# Wait until at least two parts are known. One part is not a name yet.
if count(parts) is less than 2
return
end
set newName to mash.collect(parts).join(" - ") ~ "." ~ doc.extension
if doc.name is newName
return
end
# Never overwrite a different file with the same name.
if mash.files.exists(doc.share, doc.parent.path ~ "/" ~ newName)
printline "{{newName}} already exists, leaving {{doc.name}} as it is."
return
end
set oldName to doc.name
doc.rename(newName)
printline "Renamed {{oldName}} to {{newName}}"
  1. doc.fields.has(name) is checked first, because asking for a field the share doesn’t have is an error, not null. This lets one script serve several shares that carry different fields.
  2. Skipping empty parts stops names like Acme Ltd - - pdf from appearing while Captur is still working.
  3. The two-part minimum is a policy choice. Pick whatever number means “enough to be a useful name” for your documents.
  4. doc.name == newName is what makes the script safe to run twice. A rename can fire further events for the same file, and the script must settle instead of renaming again.
  5. oldName is copied before the rename so the message can name both.
  6. The exists check refuses to overwrite another file.
  • Keep the original name. Add it at the end: parts[] = doc.display_name.
  • Add a date. Use mash.date.now().format("Y-m-d") as the first part for sortable names. See Date.
  • A counter for duplicates. When the name exists, try newName with (2), (3) added in a while loop. Cap the loop, since the run has a time limit.
  • Write the new name back to a field. doc.fields.set("Filed name", newName) records what the script did.

Library pages used: Files, Fields, String.