Use Placeholders in Upload, Download, Copy, and Move Commands
JFrog CLI provides flexibility in how you download, upload, copy, or move files through the use of wildcard or regular expressions with placeholders.
Any wildcard expression enclosed in parentheses in the source path can be matched with a corresponding placeholder (for example, {1}) in the target path. This allows you to dynamically determine the destination path and filename for an artifact.
To use placeholders in upload, download, copy, or move commands:
- Wrap the part of the source pattern you want to capture in parentheses.
- Use
{1},{2}, and so on in the target path to refer to each capture group, in order. - Run the appropriate
jf rtcommand with your pattern and target (see Using Placeholder Examples).
Prerequisites
Before running any command on this page:
- A JFrog server must be configured: run
jf config addto set one up.- The target (and source, for copy/move) repositories must already exist in Artifactory.
- For upload examples, the matching local files must be present in the current working directory.
- Use
--dry-runto preview resolved paths before committing to an actual upload. See Relevant Flags for details.
How Placeholders Work
| Component | Description |
|---|---|
(*) | Capture group in source pattern - matches any characters |
{1} | First placeholder - references first capture group |
{2} | Second placeholder - references second capture group |
{n} | Nth placeholder - references nth capture group |
Using Placeholder Examples
Example 1: Upload Files into Dynamically Named Directories
This example uploads each .tgz file from the source into a new directory in the target repository that matches the file's base name. For example, the file froggy.tgz is uploaded to my-local-repo/froggy/, which creates the froggy folder in Artifactory.
jf rt u "(*).tgz" my-local-repo/{1}/ --recursive=falseBreakdown:
| Pattern Part | Description |
|---|---|
(*).tgz | Captures the filename (without extension) into group 1 |
{1}/ | Uses the captured filename as the directory name |
--recursive=false | Only processes files in the current directory (default is true) |
Result:
| Source File | Target Path |
|---|---|
froggy.tgz | my-local-repo/froggy/froggy.tgz |
ribbit.tgz | my-local-repo/ribbit/ribbit.tgz |
Tip
Use
--dry-runto preview the resolved target paths before uploading:jf rt u "(*).tgz" my-local-repo/{1}/ --recursive=false --dry-runNo files are uploaded. The CLI prints the paths it would have used, letting you verify the placeholder logic is correct.
Example 2: Upload Files with Modified Names
This example uploads all files with names that begin with frog to the frogfiles folder, appending the suffix -up to each filename. For example, a file named froggy.tgz is renamed to froggy.tgz-up.
jf rt u "(frog*)" my-local-repo/frogfiles/{1}-up --recursive=falseBreakdown:
| Pattern Part | Description |
|---|---|
(frog*) | Captures filenames starting with "frog" into group 1 |
{1}-up | Appends "-up" suffix to the captured filename |
--recursive=false | Only processes files in the current directory |
Result:
| Source File | Target Path |
|---|---|
froggy.tgz | my-local-repo/frogfiles/froggy.tgz-up |
frog-lib.jar | my-local-repo/frogfiles/frog-lib.jar-up |
Example 3: Organize Uploaded Files by Extension
This example uploads all files from the current directory into the my-local-repo repository, placing them into subdirectories named after their file extensions.
jf rt u "(*).(*)" my-local-repo/{2}/{1}.{2} --recursive=falseBreakdown:
| Pattern Part | Description |
|---|---|
(*).(*) | Captures filename (group 1) and extension (group 2) |
{2}/ | Uses extension as directory name |
{1}.{2} | Reconstructs original filename |
--recursive=false | Only processes files in the current directory |
Result:
| Source File | Target Path |
|---|---|
froggy.tgz | my-local-repo/tgz/froggy.tgz |
ribbit.zip | my-local-repo/zip/ribbit.zip |
pond.jar | my-local-repo/jar/pond.jar |
Note
The
(*)wildcard matches any characters including hyphens. A file likemyapp-sources.jaris captured as group 1 =myapp-sourcesand group 2 =jar, and is uploaded tomy-local-repo/jar/myapp-sources.jar. Only the last dot is treated as the separator between the two groups.
Example 4: Copy Files and Append a Suffix
This example copies all .zip files from the rabbit/ directory in the source-frog-repo to the same path in the target-frog-repo and appends .cp to each copied filename.
jf rt cp "source-frog-repo/rabbit/(*.zip)" target-frog-repo/rabbit/{1}.cpBreakdown:
| Pattern Part | Description |
|---|---|
source-frog-repo/rabbit/(*.zip) | Captures .zip filenames from source path |
target-frog-repo/rabbit/{1}.cp | Places files in target with .cp suffix |
Result:
| Source Path | Target Path |
|---|---|
source-frog-repo/rabbit/archive.zip | target-frog-repo/rabbit/archive.zip.cp |
source-frog-repo/rabbit/data.zip | target-frog-repo/rabbit/data.zip.cp |
Command Reference
Supported Commands
Placeholders are supported in the following commands:
| Command | Abbreviation | Description |
|---|---|---|
jf rt upload | jf rt u | Upload files to Artifactory |
jf rt download | jf rt dl | Download files from Artifactory |
jf rt copy | jf rt cp | Copy files within Artifactory |
jf rt move | jf rt mv | Move files within Artifactory |
Relevant Flags
| Flag | Default | Description |
|---|---|---|
--recursive | true | When true, collects files from sub-folders. Set to false to process only the current directory. |
--flat | false | When true, ignores source directory structure. When false, preserves hierarchy. |
--regexp | false | When true, interprets the pattern as a regular expression instead of wildcards. |
--dry-run | false | When true, disables communication with Artifactory and prints the resolved target paths without uploading. Highly recommended when testing new placeholder patterns. |
--fail-no-op | false | When true, returns exit code 2 if no files match the source pattern. Useful in CI pipelines to catch typos in patterns before they silently no-op. |
--retries | 3 | Number of HTTP retry attempts per file on transient failures. |
--retry-wait-time | 0 | Time to wait between retries (for example, 10s or 500ms). |
Advanced Usage
Download with Placeholders
Placeholders work in downloads too. The following example downloads all .tgz files from my-local-repo and recreates a local directory per artifact base name, mirroring the structure used in Example 1:
jf rt dl "my-local-repo/(*).tgz" ./{1}/ --recursive=false| Source Path in Artifactory | Downloaded to |
|---|---|
my-local-repo/froggy/froggy.tgz | ./froggy/froggy.tgz |
my-local-repo/ribbit/ribbit.tgz | ./ribbit/ribbit.tgz |
Using Regular Expressions with Placeholders
When using --regexp, you can use regex capture groups instead of wildcard patterns:
jf rt u "(.+)-sources\.jar" libs-release-local/{1}/ --regexpThis captures the artifact name before -sources.jar and uses it as a directory.
Multiple Capture Groups
You can use multiple capture groups and reference them in any order in the target path. This lets you restructure the directory hierarchy of artifacts as part of the upload.
The following example reads files organized as <org>/<version>/<artifact>.jar locally and uploads them into my-repo/<artifact>/<org>/<version>/, effectively swapping the directory order:
jf rt u "(*)/(*)/(*).jar" my-repo/{3}/{1}/{2}.jar| Source Path | Target Path |
|---|---|
com.example/1.0/mylib.jar | my-repo/mylib/com.example/1.0.jar |
org.apache/2.3/commons.jar | my-repo/commons/org.apache/2.3.jar |
Note
Groups are numbered left to right from the source pattern.
{1}is the first(*),{2}the second, and so on. You can reference them in any order — or omit them — in the target path.
File Spec Usage
Placeholders also work in File Specs. Save the JSON below to a file (for example, upload-spec.json), then pass it to the upload command using --spec:
{
"files": [
{
"pattern": "(*).tgz",
"target": "my-local-repo/{1}/",
"recursive": "false"
}
]
}jf rt u --spec upload-spec.jsonFile Specs are useful when you need to express complex multi-pattern uploads or reuse the same pattern across scripts. All placeholder syntax supported in inline commands works identically in File Specs.
Troubleshooting
Pattern matches no files
If your pattern matches zero files, the command exits 0 with no output and no error. To catch this in CI pipelines, add --fail-no-op:
jf rt u "(*).tgz" my-local-repo/{1}/ --recursive=false --fail-no-opExit code 2 is returned when no files are matched, signaling the pipeline to fail.
Upload retries on failure
When an upload fails (for example, due to a transient network error), the CLI automatically retries up to 3 times per file. You will see [Warn] (Attempt N) messages in the output for each retry. This is expected behavior. To adjust:
--retries N— set the number of retry attempts (default:3)--retry-wait-time 5s— add a pause between retries (acceptssfor seconds,msfor milliseconds)
Verifying placeholder resolution
Use --dry-run to print the resolved target paths without uploading anything:
jf rt u "(*).(*)" my-local-repo/{2}/{1}.{2} --recursive=false --dry-runUpdated about 1 month ago
