Skip to main content
Back to blog
tutorials 5 February 2026 6 min read

Editing JMX Files from the Command Line

Techniques for modifying Apache JMeter test plans from the terminal using sed, awk, and nano - and when these approaches fall short.

M

Mark

Performance Testing Expert

JMeter test plans are stored as XML files with the .jmx extension. While the JMeter GUI is the standard way to edit these files, there are situations where command-line editing becomes necessary: headless servers, CI/CD pipelines, batch modifications across multiple files, or simply not wanting to wait for the GUI to load for a quick change.

This article explores traditional CLI approaches to JMX editing, their limitations, and when you might need something more robust.

Understanding JMX File Structure

Before diving into editing, it helps to understand what we’re working with. A JMX file is XML with a specific structure:

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <!-- More properties... -->
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group">
        <stringProp name="ThreadGroup.num_threads">10</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <!-- More properties... -->
      </ThreadGroup>
      <!-- More nested elements... -->
    </hashTree>
  </hashTree>
</jmeterTestPlan>

The nested hashTree elements represent parent-child relationships. This structure makes targeted edits with text-processing tools challenging.

Using nano for Quick Edits

For simple, one-off changes, nano works when you know exactly what you’re looking for:

nano test.jmx

Use Ctrl+W to search for specific values. For example, searching for num_threads will take you to the thread count property.

Pros:

  • Visual feedback
  • Undo support (Alt+U)
  • No risk of regex mistakes

Cons:

  • Tedious for multiple files
  • Not scriptable
  • Still requires knowing the XML structure

Using sed for Text Substitution

sed is the go-to tool for scripted text replacement. Here’s how to change the thread count:

# Change thread count from 10 to 50
sed -i 's/<stringProp name="ThreadGroup.num_threads">10</<stringProp name="ThreadGroup.num_threads">50</g' test.jmx

This works, but notice the fragility. We’re matching the entire opening tag and value to ensure we hit the right property. If the file has different formatting or whitespace, the match fails silently.

Batch Updates with sed

To update multiple JMX files:

for file in *.jmx; do
  sed -i 's/<stringProp name="ThreadGroup.num_threads">10</<stringProp name="ThreadGroup.num_threads">50</g' "$file"
done

The Whitespace Problem

JMX files can have varying indentation. This simple substitution:

sed -i 's/num_threads">10</num_threads">50</g' test.jmx

Might match unintended properties if another element happens to contain similar text. XML doesn’t care about whitespace, but your regex does.

Using awk for More Complex Patterns

awk provides better control for multi-line patterns:

# Extract current thread count
awk -F'[<>]' '/ThreadGroup.num_threads/{print $3}' test.jmx

To replace values with awk:

awk '{
  if ($0 ~ /name="ThreadGroup.num_threads"/) {
    gsub(/>10</, ">50<")
  }
  print
}' test.jmx > test_modified.jmx

This is safer than blind substitution, but still doesn’t understand XML structure. If you have multiple Thread Groups, this changes all of them.

Targeting Specific Elements

What if you need to change the thread count for only one Thread Group? With sed or awk, this requires tracking context:

# Change thread count only in "API Thread Group", not "Web Thread Group"
awk '
  /testname="API Thread Group"/ { in_target=1 }
  /testname="Web Thread Group"/ { in_target=0 }
  in_target && /ThreadGroup.num_threads/ {
    gsub(/>10</, ">50<")
  }
  { print }
' test.jmx > test_modified.jmx

This works for simple cases but becomes unwieldy as complexity grows. What if “API Thread Group” appears in a comment? What if the Thread Groups are nested differently?

Using xmllint and xmlstarlet

For proper XML manipulation, dedicated tools exist:

# Extract value with xmlstarlet
xmlstarlet sel -t -v "//ThreadGroup[@testname='Thread Group']/stringProp[@name='ThreadGroup.num_threads']" test.jmx

# Update value
xmlstarlet ed -u "//ThreadGroup[@testname='Thread Group']/stringProp[@name='ThreadGroup.num_threads']" -v "50" test.jmx > test_modified.jmx

This is XML-aware and handles structure correctly. However, JMX files use hashTree for parent-child relationships rather than direct nesting, which makes XPath expressions complex:

# Finding elements by path requires understanding hashTree relationships
xmlstarlet sel -t -v "//TestPlan/following-sibling::hashTree/ThreadGroup/stringProp[@name='ThreadGroup.num_threads']" test.jmx

Not intuitive, and getting the XPath wrong produces silent failures or incorrect matches.

Common CI/CD Patterns

Despite the challenges, these approaches are widely used in pipelines:

#!/bin/bash
# ci-update-jmx.sh - Parameterize JMX for CI/CD

THREADS=${THREADS:-10}
DURATION=${DURATION:-300}
TARGET_HOST=${TARGET_HOST:-localhost}

for file in tests/*.jmx; do
  # Update thread count
  sed -i "s/<stringProp name=\"ThreadGroup.num_threads\">[0-9]*</<stringProp name=\"ThreadGroup.num_threads\">${THREADS}</g" "$file"

  # Update duration
  sed -i "s/<stringProp name=\"ThreadGroup.duration\">[0-9]*</<stringProp name=\"ThreadGroup.duration\">${DURATION}</g" "$file"

  # Update target host
  sed -i "s/<stringProp name=\"HTTPSampler.domain\">[^<]*</<stringProp name=\"HTTPSampler.domain\">${TARGET_HOST}</g" "$file"
done

This script is fragile. If a JMX file uses intProp instead of stringProp for a value, the substitution silently fails. If the host contains special characters, the regex breaks.

When Text Tools Aren’t Enough

The limitations become apparent when you need to:

  • Add new elements - Inserting a new HTTP Request or Timer requires understanding the hashTree structure
  • Delete elements - Removing a sampler without breaking the tree is error-prone
  • Enable/disable elements - The enabled flag is an attribute, not a child element
  • Rename elements - The testname attribute affects how JMeter displays the element
  • Work with duplicate names - Multiple “HTTP Request” elements require positional awareness
# This doesn't work - can't add elements with sed/awk safely
# You'd need to know exactly where in the hashTree to insert

Alternative: Property Files

For parameterization, JMeter’s property file approach avoids editing JMX entirely:

# Run with external properties
jmeter -n -t test.jmx -Jthreads=50 -Jduration=300 -Jhost=api.example.com

This requires the JMX to reference properties:

<stringProp name="ThreadGroup.num_threads">${__P(threads,10)}</stringProp>

Clean for parameterization, but doesn’t help when you need to modify the test plan structure itself.

The Trade-offs

ApproachBest ForLimitations
nanoQuick one-off editsNot scriptable
sedSimple value substitutionNo XML awareness, fragile
awkPattern-based changesComplex for nested structures
xmlstarletXML-aware queriesJMX hashTree structure is awkward
Property filesRuntime parameterizationCan’t modify structure

Conclusion

Traditional CLI tools can handle basic JMX modifications, but they treat the file as text rather than a structured test plan. For simple substitutions in CI/CD pipelines, sed scripts work if you’re careful. For anything structural, you either need deep familiarity with XPath and JMX internals, or a tool that understands JMeter’s element hierarchy.

If you frequently edit JMX files from the command line, investing in proper tooling that understands JMeter’s structure can eliminate the guesswork and reduce the risk of corrupted test plans.

Further Reading

Tags:

#jmeter #linux #bash #command-line #ci-cd #automation

Need help with performance testing?

Let's discuss how I can help improve your application's performance.

Get in Touch