Scripts to copy file/folder name(s) or path(s) with automatic surrounding quotes based on spaces

I created a couple of scripts that dynamically copy the file names or paths of selected files and folders. One script is for names, the other is for paths.

They work like this:

  • If a single file or folder is selected, it will copy the name/path with surrounding double quotes if there is a space anywhere in the name/path, and without surrounding quotes otherwise.
  • If multiple files/folders are selected, all of their names/paths will be copied as a list, one per line. Basically like how "Clipboard COPYNAMES" works now, plus some other options below.

Variable Options:

  • Customizable Single-Item Quoting (both scripts): If a single file/folder is selected, the singleItemQuoteMode variable lets you choose how it will be quoted:
    • "auto" - Add surrounding quotes only if there are spaces
    • "never" - Don't add surrounding quotes even if there are spaces
    • "always" - Always add surrounding quotes
  • Customizable multi-line quoting (both scripts): If multiple files/folders are selected, the multiLineQuoteMode string variable in each script can be set to customize how each line will be quoted. This doesn't affect anything if only one file/folder is selected.
    • "auto" - Add quotes for lines with spaces in file name
    • "never" - Never add quotes when multiple selected
    • "always" - Add surrounding quotes to all lines
  • Optional terminating character for folders (paths script only): For any folder(s) selected, you can set the includeTrailingTerminator string variable to be whatever character you want to appear at the end of the full path.
    • To include one, for example on Windows it most likely should be a backslash, so you'd do includeTrailingTerminator = "\\"; (need to escape backslashes so there's two) and it would look like C:\Path\ instead of C:\Path.
    • To not include one, just set it to an empty string like: includeTrailingTerminator = "";
  • Optional resolving of symbolic links & junctions (paths script only): The resolveSymlinks variable allows you to specify whether to resolve symbolic links and junctions to their target paths when copying paths. This does not apply to shortcuts (.lnk files).
    • "none" - Do not resolve symbolic links or junctions
    • "symlinkedFiles" - Resolve symbolic links for files only
    • "symlinkedFolders" - Resolve symbolic links for folders only
    • "both" - Resolve symbolic links for both files and folders

Optional Command Arguments For Above Variables:

  • SINGLE_ITEM_QUOTE_MODE
  • MULTILINE_QUOTE_MODE
  • FOLDER_TERMINATOR (File paths script only)
  • RESOLVE_SYMLINKS (File paths script only)

Arguments Template (Must put this in the 'Template' box in the command editor to use arguments):

  • MULTILINE_QUOTE_MODE/O,SINGLE_ITEM_QUOTE_MODE/O,FOLDER_TERMINATOR/O,RESOLVE_SYMLINKS/O

Example - Basic argument usage when calling script as a user command:

  • Copy_File_Names_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="auto" RESOLVE_SYMLINKS="both"

  • Copy_File_Paths_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="auto" FOLDER_TERMINATOR="" RESOLVE_SYMLINKS="none"

Example: Using arguments to modify script behavior when certain keys like 'shift' are held down (see documentation about @keydown modifier). Useful if you occasionally want to force it to copy with/without quotes. You'd add the following code to a button for example (assuming you put the script in a user command called "Copy_File_Names_Auto_Quoted"). Same idea would apply using the file path script.

@keydown:none
Copy_File_Names_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="auto"
@keydown:shift
Copy_File_Names_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="never"

Here's are the scripts:

Copy_File_Names_Auto_Quoted:

// Copies files or folders names to clipboard with quotes around it if it has spaces. Various optional behavior for multiple selected items
// By ThioJoe
// Updated: 6/13/24

// Discussion Thread / Latest Version: https://resource.dopus.com/t/scripts-to-copy-file-folder-name-s-or-path-s-with-automatic-surrounding-quotes-based-on-spaces/51122
// Updates also available on my GitHub repo: https://github.com/ThioJoe/D-Opus-Scripts

//   Arguments Template (Must put this in the 'Template' box in the command editor to use arguments):
//   MULTILINE_QUOTE_MODE/O,SINGLE_ITEM_QUOTE_MODE/O

//   Example usage of arguments:
//   Copy_File_Names_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="auto"

function OnClick(clickData) {
    //------------ Options (Note: If arguments are used when calling the script, these values will be overrided by the arguments)  ------------
    // multiLineQuoteMode: Affects how quotes are added to each file name when multiple are selected
    // Set to 'never' to never add quotes when multiple selected. 'always' to add to all lines. 'auto' to add for lines with spaces in file name
    // >  Optional Argument Name: MULTILINE_QUOTE_MODE (string value)
    var multiLineQuoteMode = "never";
    // singleItemQuoteMode: Affects how quotes are added to each file name when a single file/folder is selected
    // >  Optional Argument Name: SINGLE_ITEM_QUOTE_MODE (string value)
    var singleItemQuoteMode = "auto";
    //---------------------------------
    
    var tab = clickData.func.sourcetab;
    var selectedItems = tab.selected;

    if (selectedItems.count == 0) {
        return; // No files selected, nothing to do.
    }

    if (clickData.func.args.got_arg.SINGLE_ITEM_QUOTE_MODE) {
        //Validate argument value
        argString = clickData.func.args.SINGLE_ITEM_QUOTE_MODE.toLowerCase();
        if (argString == "never" || argString == "always" || argString == "auto") {
            singleItemQuoteMode = argString;
        } else {
            singleItemQuoteMode = "auto";
            DOpus.Output("ERROR: Invalid SINGLE_ITEM_QUOTE_MODE argument. Must be either 'never', 'always', or 'auto'. Got: " + argString);
        }
        //DOpus.Output("Received SINGLE_ITEM_QUOTE_MODE argument: " + String(clickData.func.args.SINGLE_ITEM_QUOTE_MODE));
    }

    if (clickData.func.args.got_arg.MULTILINE_QUOTE_MODE) {
        //Validate argument value
        argString = clickData.func.args.MULTILINE_QUOTE_MODE.toLowerCase();
        if (argString == "never" || argString == "always" || argString == "auto") {
            multiLineQuoteMode = argString;
        } else {
            multiLineQuoteMode = "never";
            DOpus.Output("ERROR: Invalid MULTILINE_QUOTE_MODE argument. Must be either 'never', 'always', or 'auto'. Got: " + argString);
        }
        //DOpus.Output("Received MULTILINE_QUOTE_MODE argument: " + String(clickData.func.args.MULTILINE_QUOTE_MODE));
    }

    var clipboardText = "";
    // If single item is selected
    if (selectedItems.count == 1) {
        var singleItem = selectedItems(0);
        // If no spaces in the filename or option set to not use quotes
        if (singleItemQuoteMode != "always" && (singleItem.name.indexOf(" ") == -1 || singleItemQuoteMode == "never")) {
            clipboardText = singleItem.name;
        } else {
            // Filename contains spaces or option set to always use quotes
            clipboardText = '"' + singleItem.name + '"';
        }
    // Multiple files are selected
    } else {
        for (var i = 0; i < selectedItems.count; i++) {
            var fileName = selectedItems(i).name;
            //Add a newline character to the beginning starting after the first line
            if (i > 0) {
                clipboardText += "\n";
            }
            if (multiLineQuoteMode === "always") {
                clipboardText += '"' + fileName + '"';
            } else if (multiLineQuoteMode === "auto" && fileName.indexOf(" ") !== -1) {
                clipboardText += '"' + fileName + '"';
            } else {
                clipboardText += fileName;
            }
        }
    }

    DOpus.SetClip(clipboardText);
    // For debugging:
    //DOpus.Output("--- Copied to clipboard: ---\n" + clipboardText);
}

Copy_File_Paths_Auto_Quoted:

// Copies files or folders full paths to clipboard with quotes around it if it has spaces. Various optional behavior for multiple selected items
// By ThioJoe
// Updated: 6/21/24

// Discussion Thread / Latest Version: https://resource.dopus.com/t/scripts-to-copy-file-folder-name-s-or-path-s-with-automatic-surrounding-quotes-based-on-spaces/51122
// Updates also available on my GitHub repo: https://github.com/ThioJoe/D-Opus-Scripts

//   Arguments Template (Must put this in the 'Template' box in the command editor to use arguments):
//   MULTILINE_QUOTE_MODE/O,SINGLE_ITEM_QUOTE_MODE/O,FOLDER_TERMINATOR/O,RESOLVE_SYMLINKS/O

//   Example usage of arguments:
//   Copy_File_Paths_Auto_Quoted MULTILINE_QUOTE_MODE="never" SINGLE_ITEM_QUOTE_MODE="auto" FOLDER_TERMINATOR="\" RESOLVE_SYMLINKS="both"

function OnClick(clickData) {
    //------------ Options (Note: If arguments are used when calling the script, these values will be overrided by the arguments) ------------
    // multiLineQuoteMode: Affects how quotes are added around each file path when multiple are selected
    // Set to 'never' to never add quotes when multiple selected. 'always' to add to all lines. 'auto' to add for lines with spaces in file path
    // >  Optional Argument Name: MULTILINE_QUOTE_MODE (string value)
    var multiLineQuoteMode = "never";
    // Which trailing path terminator to add (if any) to the end of full paths of folders (basically like the 'noterm' modifier)
    // Just set to empty string if none wanted. Don't forget to escape as necessary (to add a backslash would be like: "\\")
    // >  Optional Argument Name: FOLDER_TERMINATOR (string value)
    var includeTrailingTerminator = "";
    // singleItemQuoteMode: Affects how quotes are added to each file name when a single file/folder is selected
    // >  Optional Argument Name: SINGLE_ITEM_QUOTE_MODE (string value)
    var singleItemQuoteMode = "auto";
    // resolveSymlinksAndJunctions: Whether to copy the resulting linked target path instead of the 'virtual' file/folder for symbolic links (can be either files/folders) and junctions (folders).
    // Note: Doesn't apply to shortcuts (.lnk files)
    // Possible values: none, symlinkedFiles, symlinkedFolders, both
    // >  Optional Argument Name: RESOLVE_SYMLINKS (string value)
    var resolveSymlinks = "none";
    //---------------------------------
    
    var tab = clickData.func.sourcetab;
    var selectedItems = tab.selected;

    if (selectedItems.count == 0) {
        return; // No files selected, nothing to do.
    }

    if (clickData.func.args.got_arg.SINGLE_ITEM_QUOTE_MODE) {
        //Validate argument value
        argString = clickData.func.args.SINGLE_ITEM_QUOTE_MODE.toLowerCase();
        if (argString == "never" || argString == "always" || argString == "auto") {
            singleItemQuoteMode = argString;
        } else {
            singleItemQuoteMode = "auto";
            DOpus.Output("ERROR: Invalid SINGLE_ITEM_QUOTE_MODE argument. Must be either 'never', 'always', or 'auto'. Got: " + argString);
        }
        //DOpus.Output("Received SINGLE_ITEM_QUOTE_MODE argument: " + String(clickData.func.args.SINGLE_ITEM_QUOTE_MODE));
    }

    if (clickData.func.args.got_arg.MULTILINE_QUOTE_MODE) {
        //Validate argument value
        argString = clickData.func.args.MULTILINE_QUOTE_MODE.toLowerCase();
        if (argString == "never" || argString == "always" || argString == "auto") {
            multiLineQuoteMode = argString;
        } else {
            multiLineQuoteMode = "never";
            DOpus.Output("ERROR: Invalid MULTILINE_QUOTE_MODE argument. Must be either 'never', 'always', or 'auto'. Got: " + argString);
        }
        //DOpus.Output("Received MULTILINE_QUOTE_MODE argument: " + String(clickData.func.args.MULTILINE_QUOTE_MODE));
    }

    if (clickData.func.args.got_arg.RESOLVE_SYMLINKS) {
        //Validate argument value
        argString = clickData.func.args.RESOLVE_SYMLINKS.toLowerCase();
        if (argString == "none" || argString == "symlinkedfiles" || argString == "symlinkedfolders" || argString == "both") {
            resolveSymlinks = argString;
        } else {
            resolveSymlinks = "none";
            DOpus.Output("ERROR: Invalid RESOLVE_SYMLINKS argument. Must be either 'none', 'symlinkedFiles', 'symlinkedFolders', or 'both'. Got: " + argString);
        }
        //DOpus.Output("Received RESOLVE_SYMLINKS argument: " + String(clickData.func.args.RESOLVE_SYMLINKS));
    }

    if (clickData.func.args.got_arg.FOLDER_TERMINATOR) {
        includeTrailingTerminator = clickData.func.args.FOLDER_TERMINATOR;
        //DOpus.Output("Received FOLDER_TERMINATOR argument");
    }

    var clipboardText = "";
    // If single item is selected
    if (selectedItems.count == 1) {
        var singleItem = selectedItems(0);
        var filePath = String(singleItem.realpath);
        
        // Resolve symlink/junction if needed
        if (resolveSymlinks == "both" || (resolveSymlinks == "symlinkedfiles" && !singleItem.is_dir) || (resolveSymlinks == "symlinkedfolders" && singleItem.is_dir)) {
            var resolvedPath = DOpus.FSUtil.Resolve(String(singleItem.realpath), "j");
            filePath = String(resolvedPath);
        }
       
        if (singleItem.is_dir) {
            filePath += includeTrailingTerminator;
        }
        // If no spaces in the file path or option set to not use quotes
        if (singleItemQuoteMode != "always" && (filePath.indexOf(" ") == -1 || singleItemQuoteMode == "never")) {
            clipboardText = filePath;
        } else {
            // File path contains spaces or option set to always use quotes
            clipboardText = '"' + filePath + '"';
        }
    } else {
        // Multiple items selected
        for (var i = 0; i < selectedItems.count; i++) {
            var filePath = String(selectedItems(i).realpath);

            // Resolve symlink/junction if needed
            if (resolveSymlinks == "both" || (resolveSymlinks == "symlinkedfiles" && !selectedItems(i).is_dir) || (resolveSymlinks == "symlinkedfolders" && selectedItems(i).is_dir)) {
                var resolvedPath = DOpus.FSUtil.Resolve(String(selectedItems(i).realpath), "j");
                filePath = String(resolvedPath);
            }
            
            if (selectedItems(i).is_dir) {
                filePath += includeTrailingTerminator;
            }
            //Add a newline character to the beginning starting after the first line
            if (i > 0) {
                clipboardText += "\n";
            }
            if (multiLineQuoteMode === "always") {
                clipboardText += '"' + filePath + '"';
            } else if (multiLineQuoteMode === "auto" && filePath.indexOf(" ") !== -1) {
                clipboardText += '"' + filePath + '"';
            } else {
                clipboardText += filePath;
            }
        }
    }

    DOpus.SetClip(clipboardText);
    // For debugging:
    //DOpus.Output("--- Copied to clipboard: ---\n" + clipboardText);
}
3 Likes

How should I add this script in Directory Opus?

This may help: How to use buttons and scripts from this forum - #2 by Leo

1 Like

should I copy and paste the script?
Or save as ? which extension

Thanks it works perfectly

I've just updated the scripts to add optional behavior when a single item is selected (auto, always, never), just like there already was for multiple items.

I've also added command arguments that can be used to modify any of the variables when calling the script. For example, this allows you to use the arguments in in conjunction with the @keydown modifier so you can override the automatic quoting behavior. I've added an example to the post above.