Problem
Sometimes a Linux server may develop thousands of duplicate entries in the root user's PATH environment variable.
A common example is:
export PATH=$PATH:/usr/local/bin
If this line is repeatedly appended to /root/.bashrc, every new shell adds /usr/local/bin again.
Eventually, the PATH can become extremely large, causing commands such as:
whoami
env
wc
to fail with errors such as:
Argument list too long
or other environment-size-related errors.
Symptoms
Check the current PATH:
echo "$PATH"
Check the environment size:
env | wc -c
A problematic server may show /usr/local/bin hundreds or thousands of times.
Check /root/.bashrc:
grep -nF 'export PATH=$PATH:/usr/local/bin' /root/.bashrc
Example:
/root/.bashrc:8756:export PATH=$PATH:/usr/local/bin
/root/.bashrc:8757:export PATH=$PATH:/usr/local/bin
/root/.bashrc:8758:export PATH=$PATH:/usr/local/bin
Manual Fix
1. Reset the Current Shell PATH First
If the current PATH is extremely large, external commands such as sed, awk, or env may fail.
Therefore, reset PATH before running cleanup commands:
export PATH="/usr/local/cpanel/3rdparty/lib/path-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
For systems without cPanel, this is sufficient:
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Verify:
echo "$PATH"
2. Backup .bashrc
Before modifying the production server:
cp -a /root/.bashrc /root/.bashrc.backup.$(date +%F-%H%M%S)
3. Remove Duplicate Entries
The following command removes all matching lines:
sed -i '/^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/d' /root/.bashrc
Verify:
grep -nF 'export PATH=$PATH:/usr/local/bin' /root/.bashrc
No output means no matching line remains.
This method removes the line completely. The automated method below is safer for repeated maintenance because it keeps one copy when duplicates exist.
Automated Fix — Recommended
For production servers, use the following Bash script.
It will:
-
Reset the current script's
PATHbefore running external commands. -
Check whether
/root/.bashrcexists. -
Count the problematic entries.
-
Keep one entry if multiple copies exist.
-
Do nothing if zero or one entry exists.
-
Use a uniquely named temporary file.
-
Preserve the original owner/group.
-
Preserve the original permissions.
-
Write the new file completely before replacement.
-
Replace the original using
mv. -
Remove the temporary file if an operation fails.
-
Print a simple status message.
fix-path.sh
#!/bin/bash
# ============================================================
# Linux PATH Duplicate Cleanup
# ============================================================
# Always use a safe PATH before executing external commands.
PATH="/usr/local/cpanel/3rdparty/lib/path-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH
FILE="/root/.bashrc"
# ------------------------------------------------------------
# Check target file
# ------------------------------------------------------------
if [ ! -f "$FILE" ]; then
echo "CleanPath: skipped - $FILE not found"
exit 0
fi
# ------------------------------------------------------------
# Count problematic entries
# ------------------------------------------------------------
COUNT=$(awk '
$0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/ {
count++
}
END {
print count + 0
}
' "$FILE" 2>/dev/null)
if [ -z "$COUNT" ]; then
echo "CleanPath: skipped - unable to read $FILE"
exit 0
fi
# ------------------------------------------------------------
# Nothing to clean
# ------------------------------------------------------------
if [ "$COUNT" -le 1 ]; then
echo "CleanPath: already clean"
exit 0
fi
# ------------------------------------------------------------
# Create a unique temporary file
# ------------------------------------------------------------
TMP=$(mktemp "${FILE}.tmp.XXXXXX") || {
echo "CleanPath: failed - unable to create temporary file"
exit 0
}
# ------------------------------------------------------------
# Preserve ownership and permissions
# ------------------------------------------------------------
chown --reference="$FILE" "$TMP" 2>/dev/null || true
chmod --reference="$FILE" "$TMP" 2>/dev/null || true
# ------------------------------------------------------------
# Keep only the first matching PATH entry
# ------------------------------------------------------------
if ! awk '
BEGIN {
found=0
}
{
if ($0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/) {
found++
if (found > 1)
next
}
print
}
' "$FILE" > "$TMP"; then
rm -f "$TMP"
echo "CleanPath: failed - unable to process $FILE"
exit 0
fi
# ------------------------------------------------------------
# Safety check
# ------------------------------------------------------------
if ! cmp -s "$FILE" "$TMP"; then
# Atomic replacement
if mv -f "$TMP" "$FILE"; then
echo "CleanPath: removed $((COUNT - 1)) duplicate entries"
else
rm -f "$TMP"
echo "CleanPath: failed - unable to replace $FILE"
fi
else
rm -f "$TMP"
echo "CleanPath: already clean"
fi
exit 0
Install the Automated Script
Create:
nano /usr/local/sbin/fix-path.sh
Paste the script and save it.
Make it executable:
chmod 700 /usr/local/sbin/fix-path.sh
Run:
/usr/local/sbin/fix-path.sh
Example output when duplicates exist:
CleanPath: removed 384 duplicate entries
If only one entry exists:
CleanPath: already clean
If there are no problematic entries:
CleanPath: already clean
One-Command Automated Installation
The script can also be installed using:
cat > /usr/local/sbin/fix-path.sh <<'EOF'
#!/bin/bash
PATH="/usr/local/cpanel/3rdparty/lib/path-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH
FILE="/root/.bashrc"
if [ ! -f "$FILE" ]; then
echo "CleanPath: skipped - $FILE not found"
exit 0
fi
COUNT=$(awk '
$0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/ {
count++
}
END {
print count + 0
}
' "$FILE" 2>/dev/null)
if [ -z "$COUNT" ]; then
echo "CleanPath: skipped - unable to read $FILE"
exit 0
fi
if [ "$COUNT" -le 1 ]; then
echo "CleanPath: already clean"
exit 0
fi
TMP=$(mktemp "${FILE}.tmp.XXXXXX") || {
echo "CleanPath: failed - unable to create temporary file"
exit 0
}
chown --reference="$FILE" "$TMP" 2>/dev/null || true
chmod --reference="$FILE" "$TMP" 2>/dev/null || true
if ! awk '
BEGIN { found=0 }
{
if ($0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/) {
found++
if (found > 1)
next
}
print
}
' "$FILE" > "$TMP"; then
rm -f "$TMP"
echo "CleanPath: failed - unable to process $FILE"
exit 0
fi
if cmp -s "$FILE" "$TMP"; then
rm -f "$TMP"
echo "CleanPath: already clean"
exit 0
fi
if mv -f "$TMP" "$FILE"; then
echo "CleanPath: removed $((COUNT - 1)) duplicate entries"
else
rm -f "$TMP"
echo "CleanPath: failed - unable to replace $FILE"
fi
EOF
chmod 700 /usr/local/sbin/fix-path.sh
Run:
/usr/local/sbin/fix-path.sh
Go Application Version
For a Go-based licensing/agent application, the same logic can be called through Shellout():
func CleanPath() {
Shellout(`PATH="/usr/local/cpanel/3rdparty/lib/path-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; export PATH
FILE="/root/.bashrc"
if [ ! -f "$FILE" ]; then
echo "CleanPath: skipped - file not found"
exit 0
fi
COUNT=$(awk '
$0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/ {
count++
}
END {
print count + 0
}' "$FILE" 2>/dev/null)
[ "$COUNT" -le 1 ] && {
echo "CleanPath: already clean"
exit 0
}
TMP=$(mktemp "${FILE}.tmp.XXXXXX") || {
echo "CleanPath: failed"
exit 0
}
chown --reference="$FILE" "$TMP" 2>/dev/null || true
chmod --reference="$FILE" "$TMP" 2>/dev/null || true
if ! awk '
BEGIN { found=0 }
{
if ($0 ~ /^[[:space:]]*export PATH=\$PATH:\/usr\/local\/bin[[:space:]]*$/) {
found++
if (found > 1)
next
}
print
}
' "$FILE" > "$TMP"; then
rm -f "$TMP"
echo "CleanPath: failed"
exit 0
fi
if cmp -s "$FILE" "$TMP"; then
rm -f "$TMP"
echo "CleanPath: already clean"
exit 0
fi
if mv -f "$TMP" "$FILE"; then
echo "CleanPath: removed $((COUNT - 1)) duplicate entries"
else
rm -f "$TMP"
echo "CleanPath: failed"
fi
`)
}
Important: Why PATH Is Reset First
When the environment is already too large, commands may fail before cleanup can even start:
/usr/bin/sed: Argument list too long
/usr/bin/env: Argument list too long
Therefore the automated cleanup starts with:
PATH="/usr/local/cpanel/3rdparty/lib/path-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH
This ensures that commands such as:
awk
mktemp
cmp
mv
rm
can be located normally.
Verify After Cleanup
Run:
echo "$PATH"
Then:
env | wc -c
And:
whoami
Expected:
root
Check .bashrc:
grep -nF 'export PATH=$PATH:/usr/local/bin' /root/.bashrc
The automated version should leave at most one matching line.
Verify With a New SSH Session
Exit:
exit
Reconnect and run:
echo "$PATH"
Then:
env | wc -c
And:
whoami
Finally:
grep -nF 'export PATH=$PATH:/usr/local/bin' /root/.bashrc
Finding the Root Cause
Cleanup fixes the symptom. If another process continues adding the line, the problem may return.
Search for the exact line:
grep -RInF 'export PATH=$PATH:/usr/local/bin' \
/root /etc/cron* /etc/systemd /usr/local \
2>/dev/null | head -100
Search for scripts appending to .bashrc:
grep -RInE '(\>\>|tee[[:space:]]+-a|printf.*bashrc|echo.*bashrc).*PATH' \
/root /etc/cron* /etc/systemd /usr/local \
2>/dev/null | head -100
Check root's history:
grep -nF 'export PATH=$PATH:/usr/local/bin' \
/root/.bash_history 2>/dev/null
cPanel Note
Do not blindly remove all PATH assignments from:
/etc/profile
/etc/bashrc
/etc/profile.d/
/usr/local/cpanel/etc/bashrc
For example, cPanel may legitimately contain:
export PATH="$PATH:/usr/local/bin:/usr/X11R6/bin"
The cleanup described in this KB specifically targets:
export PATH=$PATH:/usr/local/bin
inside:
/root/.bashrc
Recommended Production Approach
For a production server or licensing/agent binary:
-
Reset
PATHbefore executing external commands. -
Backup the original
.bashrcbefore manual intervention. -
Keep one valid copy rather than deleting every matching line.
-
Use a unique temporary file.
-
Preserve owner/group and permissions.
-
Do not rewrite the file when there are zero or one matching entries.
-
Write the temporary file completely before replacing the original.
-
Use
mvfor the final replacement. -
Keep failures silent or return a simple status suitable for the application.
-
Investigate the process that originally created the duplicates if they return.
The automated script is intended to make the cleanup idempotent: running it repeatedly should not progressively modify /root/.bashrc.
