Skip to content

Instantly share code, notes, and snippets.

@akkuman
Last active February 28, 2026 09:14
Show Gist options
  • Select an option

  • Save akkuman/c1d9577fab2a8701142887de5dc04270 to your computer and use it in GitHub Desktop.

Select an option

Save akkuman/c1d9577fab2a8701142887de5dc04270 to your computer and use it in GitHub Desktop.
Clean vscode old version extension

A script to help you clean up old versions of VSCode

introduce

As is well known, this is a long-standing bug: VS Code always leaves behind old versions of extensions when upgrading extensions.

Previously, this space usage was not a problem, but with the emergence of AI programming extensions, after long-term operation, the size of the related extensions may reach the GB level, quickly filling up the disk.

The main function of this extension is to help you remove older versions of the extension on your Linux host and devcontainer, thus freeing up disk space.

Usage

curl -sL https://gist.github.com/akkuman/c1d9577fab2a8701142887de5dc04270/raw/cleanup-vscode-ext.sh | bash
#!/bin/bash
clean_script=$(cat <<'EOF'
#!/bin/bash
# Define directories to clean
declare -a dirs=(
"$HOME/.vscode/extensions"
"$HOME/.vscode-server/extensions"
)
# Get total disk capacity (using root / as example, modify mount point as needed)
get_disk_total() {
# Get total capacity of the disk partition where current directory resides
local mount_point="/" # default to root directory
if [ -d "$1" ]; then
# Get mount point for specified directory
mount_point=$(df "$1" | tail -1 | awk '{print $6}')
fi
# Get total disk capacity (bytes)
df -B1 "$mount_point" | tail -1 | awk '{print $2}'
}
# Get current disk usage
disk_total=$(get_disk_total "$HOME")
human_disk_total=$(numfmt --to=iec $disk_total)
# Table output functions
print_header() {
printf "+----------------------------------------+---------------------------------------------------------+-------------+\n"
printf "| %-38s | %-55s | %-11s |\n" "Extension" "Version" "Size"
printf "+----------------------------------------+---------------------------------------------------------+-------------+\n"
}
print_row() {
local ext="$1"
local version="$2"
local size="$3"
# Truncate long text
if [ ${#ext} -gt 38 ]; then
ext="${ext:0:35}..."
fi
if [ ${#version} -gt 55 ]; then
version="${version:0:52}..."
fi
printf "| %-38s | %-55s | %-11s |\n" "$ext" "$version" "$size"
}
print_footer() {
printf "+----------------------------------------+---------------------------------------------------------+-------------+\n"
}
echo "Total Disk Capacity: $human_disk_total"
echo ""
for dir in "${dirs[@]}"; do
# Check if directory exists
if [ ! -d "$dir" ]; then
continue
fi
echo "========================================"
echo "Processing: $dir"
echo "========================================"
cd "$dir" || { echo "Failed to enter directory: $dir"; continue; }
# Get total size of directory before cleaning
dir_size_before=$(du -sb "$dir" | cut -f1)
# Get list of unique extension name prefixes (without version numbers)
extensions=$(ls | rev | cut -d'-' -f2- | rev | uniq)
# Initialize cleaning table counter
cleaned_count=0
for ext in $extensions; do
# Get all version directories for each extension, sorted (newest last)
versions=$(ls | grep "^$ext" | sort -t. -k2,2V)
count=$(echo "$versions" | wc -l)
# If multiple versions exist, delete old ones
if [ "$count" -gt 1 ]; then
# Print table header for first cleaned extension
if [ $cleaned_count -eq 0 ]; then
echo ""
echo "Cleaning Old Extension Versions:"
print_header
fi
# Delete all old versions except the newest one
old_versions=($(echo "$versions" | head -n $((count - 1))))
for old_version in "${old_versions[@]}"; do
if [ -d "$old_version" ]; then
# Calculate size of directory to be deleted
size=$(du -sb "$old_version" | cut -f1)
human_size=$(numfmt --to=iec $size)
# Print table row
print_row "$ext" "$old_version" "$human_size"
rm -rf "$old_version"
cleaned_count=$((cleaned_count + 1))
fi
done
fi
done
# Print table footer if any extensions were cleaned
if [ $cleaned_count -gt 0 ]; then
print_footer
echo "Cleaned $cleaned_count old extension versions"
echo ""
else
echo "No old versions need cleaning"
echo ""
fi
# Calculate freed space as percentage of original directory size
dir_size_after=$(du -sb "$dir" | cut -f1)
dir_freed=$((dir_size_before - dir_size_after))
if [ "$dir_size_before" -gt 0 ]; then
dir_percent=$(echo "$dir_freed $dir_size_before" | awk '{printf "%.3f", $1 * 100 / $2}')
else
dir_percent=0
fi
# Calculate freed space as percentage of total disk capacity
if [ "$disk_total" -gt 0 ]; then
dir_disk_percent=$(echo "$dir_freed $disk_total" | awk '{printf "%.3f", $1 * 100 / $2}')
else
dir_disk_percent=0
fi
echo "----------------------------------------"
echo "Before: $(numfmt --to=iec $dir_size_before)"
echo "After : $(numfmt --to=iec $dir_size_after)"
echo "Free : $(numfmt --to=iec $dir_freed) (Directory: ${dir_percent}%, Disk: ${dir_disk_percent}%)"
echo "Finish: $dir"
echo
done
EOF
)
echo "$clean_script" | bash
echo ""
# ---------------------------
# Clean Development Container Old Extension Versions
# ---------------------------
DOCKER_CMD="sudo docker"
# Find matching containers
CONTAINERS=$($DOCKER_CMD inspect --format='{{.Config.Image}} {{.Name}} {{.State.Status}}' $(sudo docker ps -aq) | column -t | grep -E 'vsc-.+-uid')
if [[ -z "$CONTAINERS" ]]; then
echo "No containers matching pattern 'vsc-.+-uid' found"
exit 0
fi
# Define command to execute in containers
# Modify this to the command you need to execute
EXEC_CMD="$clean_script"
echo "Found matching containers:"
echo "========================================"
echo "$CONTAINERS"
echo "========================================"
echo ""
# Process each container
echo "$CONTAINERS" | while read LINE; do
# Extract container name and status
CONTAINER_NAME=$(echo $LINE | awk '{print $2}')
CONTAINER_STATE=$(echo $LINE | awk '{print $3}')
echo "----------------------------------------------------------------------------------------------------------------"
echo "----------------------------------------------------------------------------------------------------------------"
# Get devcontainer remoteUser
remoteUser=$($DOCKER_CMD inspect --format='{{json .Config.Labels}}' "$CONTAINER_NAME" | grep -oE '\\"remoteUser\\":\\"[^"]*\\"' | sed 's/.*:\\"\([^"]*\)\\"/\1/' | tail -n 1)
echo "Processing container: $CONTAINER_NAME (User: $remoteUser Status: $CONTAINER_STATE)"
WAS_STOPPED=false
# Check container status
if [[ $CONTAINER_STATE == "exited" ]]; then
if $DOCKER_CMD start "$CONTAINER_NAME" >/dev/null 2>&1; then
WAS_STOPPED=true
else
echo " ❌ Failed to start container, skipping"
continue
fi
fi
# Execute command inside container
$DOCKER_CMD exec --user "$remoteUser" "$CONTAINER_NAME" bash -c "$EXEC_CMD"
# If container was originally stopped, stop it after execution
if [[ "$WAS_STOPPED" == true ]]; then
$DOCKER_CMD stop "$CONTAINER_NAME" >/dev/null 2>&1 || echo " ❌ Failed to stop container"
fi
echo ""
done
echo "All development containers processed!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment