Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
apt-fast v1.11
Use this just like aptitude or apt-get for faster package downloading.

Copyright: 2008-2012 Matt Parnell, http://www.mattparnell.com
Copyright: 2008-2012 Matt Parnell
Improvements, maintenance, revisions - 2012, 2017-2019 Dominique Lasserre

You may distribute this file under the terms of the GNU General
Public License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
-->

apt-fast 1.9
============
apt-fast 1.11
=============
apt-fast is a shell script wrapper for apt-get and aptitude that can drastically improve apt download times by downloading packages in parallel, with multiple connections per package.

## Table of Contents
Expand Down Expand Up @@ -62,10 +62,14 @@ You can use the Ubuntu PPA to get a graphical configuration file setup and autom


### Debian and derivates ###
Some distros, such as PCLinuxOS, include apt-fast in their repositories. However, if not included like in Debian or Kali Linux, then the PPA can be manually added by creating a new file `/etc/apt/sources.list.d/apt-fast.list`:
Some distros, such as PCLinuxOS, include apt-fast in their repositories. However, if not included like in Debian or Kali Linux, then the PPA can be manually added by creating a new file `/etc/apt/sources.list.d/apt-fast.sources`:

```
deb [signed-by=/etc/apt/keyrings/apt-fast.gpg] http://ppa.launchpad.net/apt-fast/stable/ubuntu focal main
Types: deb
URIs: https://ppa.launchpadcontent.net/apt-fast/stable/ubuntu/
Suites: focal
Components: main
Signed-By: /etc/apt/keyrings/apt-fast.gpg
```

To retrieve the signed keys and install apt-fast, execute the following commands as root:
Expand Down
73 changes: 42 additions & 31 deletions apt-fast
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/bin/bash
#
# apt-fast v1.10.0
# apt-fast v1.11.0
# Use this just like aptitude or apt-get for faster package downloading.
#
# Copyright: 2008-2012 Matt Parnell, http://www.mattparnell.com
# Copyright: 2008-2012 Matt Parnell
# Improvements, maintenance, revisions - 2012, 2017-2019 Dominique Lasserre
#
# You may distribute this file under the terms of the GNU General
Expand Down Expand Up @@ -55,7 +55,8 @@ for argument in "$@"; do
option="source"
root=0
;;
changelog | list | show | help | --help | -h)
changelog | list | show | search | help | --help | -h)
option="readonly"
root=0
;;
esac
Expand Down Expand Up @@ -227,10 +228,12 @@ msg_already_running()
msg "Verify that all apt-fast processes are finished then remove $LCK_FILE.lock and try again." "hint"
}

# Check if a lock file exists.
if [ -f "$LCK_FILE.lock" ]; then
msg_already_running
exit 1
# Check if a lock file exists (skip for read-only commands).
if [ "$option" != "readonly" ]; then
if [ -f "$LCK_FILE.lock" ]; then
msg_already_running
exit 1
fi
fi


Expand Down Expand Up @@ -281,13 +284,6 @@ exit_cleanup_state()
exit $CLEANUP_STATE
}

# decode url string
# translates %xx but must not convert '+' in spaces
urldecode()
{
printf '%b' "${1//%/\\x}"
}

# Check if mirrors are available. And if so add all mirrors to download list.
get_mirrors(){
# Check all mirror lists.
Expand Down Expand Up @@ -337,15 +333,16 @@ prepare_auth(){
AUTH_INFO_PARSED+=("$machine $login $password")
done <<< "$auth_info"
done
if [ "${#AUTH_INFO_PARSED[@]}" -eq 0 ]; then
# acts as if auth is disabled when no auth info is provided to improve performance
APT_FAST_APT_AUTH=0
fi
}

# Gets URI as parameter and tries to add basic http credentials. Will fail on
# credentials that contain characters that need URL-encoding.
# Caller is responsible for checking APT_FAST_APT_AUTH before calling this function.
get_auth(){
if [ "$APT_FAST_APT_AUTH" -eq 0 ]; then
echo "$1"
return
fi
for auth_info in "${AUTH_INFO_PARSED[@]}"; do
# convert to array, don't escape variable here
auth_info_arr=($auth_info)
Expand Down Expand Up @@ -405,14 +402,21 @@ get_uris(){
while IFS=' ' read -r uri filename filesize checksum_string _
do
[ -z "$uri" ] && continue
uri="$(get_auth "${uri//"'"/}")"
uri="${uri//"'"/}"
[ "$APT_FAST_APT_AUTH" -ne 0 ] && uri="$(get_auth "$uri")"
IFS=':' read -r hash_algo checksum _ <<<"$checksum_string"

filename_decoded="$(urldecode "$filename")"
if [[ "$filename" == *%* ]]; then
# decode url string
# translates %xx but must not convert '+' in spaces
filename_decoded="$(printf '%b' "${filename//%/\\x}")"
else
# most filenames do not contain %, so skip decoding to improve performance
filename_decoded="$filename"
fi
IFS='_' read -r pkg_name_decoded pkg_version_decoded _ <<<"$filename_decoded"
DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY}$pkg_name_decoded $pkg_version_decoded"
DOWNLOAD_DISPLAY="${DOWNLOAD_DISPLAY} $(echo "$filesize" | numfmt --to=iec-i --suffix=B)\n"
DOWNLOAD_SIZE=$((DOWNLOAD_SIZE + filesize))
DOWNLOAD_DISPLAY+="$pkg_name_decoded $pkg_version_decoded $filesize\n"
((DOWNLOAD_SIZE += filesize))

## whole uri comes encoded (urlencoded). Filename must NOT be decoded because
# plain aptitude do not decode it when download and install it. Therefore, we
Expand Down Expand Up @@ -494,20 +498,24 @@ display_downloadfile(){
DISPLAY_SORT_OPTIONS=(-k 1,1)
# Sort output after package download size (decreasing):
#DISPLAY_SORT_OPTIONS=(-k 3,3 -hr)
DISPLAY_NUMFMT_OPTIONS=(--to=iec-i --suffix=B)
while IFS=' ' read -r pkg ver size _; do
[ -z "$pkg" ] && continue
printf '%s%-40s %-20s %10s\n' "$aptfast_prefix" "$pkg" "$ver" "$size"
done <<<"$(echo -e "$DOWNLOAD_DISPLAY" | sort "${DISPLAY_SORT_OPTIONS[@]}")"
done <<<"$(echo -e "$DOWNLOAD_DISPLAY" | sort "${DISPLAY_SORT_OPTIONS[@]}" | numfmt "${DISPLAY_NUMFMT_OPTIONS[@]}" --field=3)"
fi
msg "Download size: $(echo "$DOWNLOAD_SIZE" | numfmt --to=iec-i --suffix=B)" "normal"
}

# Create and insert a PID number to lockfile.
_create_lock
# Create and insert a PID number to lockfile (skip for read-only commands).
if [ "$option" != "readonly" ]; then
_create_lock
fi

# Make sure aria2c (in general first parameter from _DOWNLOADER) is available.
# Read-only commands don't use the downloader.
CMD="$(echo "$_DOWNLOADER" | sed 's/^\s*\([^ ]\+\).*$/\1/')"
if [ ! "$(command -v "$CMD")" ]; then
if [ "$option" != "readonly" ] && [ ! "$(command -v "$CMD")" ]; then
msg "Command not found: $CMD" "normal" "err"
msg "You must configure $CONFFILE to use aria2c or another supported download manager" "normal" "err"
CLEANUP_STATE=1
Expand All @@ -534,7 +542,7 @@ SHA256_SUPPORTED=
SHA1_SUPPORTED=
MD5sum_SUPPORTED=
HASH_SUPPORTED=
if [ "$CMD" == "aria2c" ]; then
if [ "$option" != "readonly" ] && [ "$CMD" == "aria2c" ]; then
for supported_hash in $(LC_ALL=C aria2c -v | sed '/^Hash Algorithms:/!d; s/\(^Hash Algorithms: \|,\)\+//g'); do
case "$supported_hash" in
sha-512) SHA512_SUPPORTED=y; HASH_SUPPORTED=y ;;
Expand Down Expand Up @@ -584,16 +592,19 @@ done
# Export the variables to make them available in subshells (aka the
# downloader command).
eval "$(apt-config shell ftp_proxy Acquire::ftp::proxy)"
export ftp_proxy
eval "$(apt-config shell http_proxy Acquire::http::proxy)"
export http_proxy
eval "$(apt-config shell https_proxy Acquire::https::proxy)"
export https_proxy
# Only export proxy variables if they are non-empty; exporting empty proxy
# variables can confuse tools like apt-listbugs with "unsupported proxy ''" errors.
[ -n "$ftp_proxy" ] && export ftp_proxy || unset ftp_proxy
[ -n "$http_proxy" ] && export http_proxy || unset http_proxy
[ -n "$https_proxy" ] && export https_proxy || unset https_proxy
Comment on lines +597 to +601
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxy handling now uses unset ftp_proxy/http_proxy/https_proxy when the value is empty. This loses the script’s earlier distinction between “set but empty” vs “unset” (preserved across sudo) and can change behavior for tools that fall back to HTTP_PROXY/HTTPS_PROXY when the lowercase vars are unset. To avoid exporting empty proxies while keeping semantics stable, prefer removing the export attribute (e.g., export -n var) or otherwise keep the shell variable defined but unexported when empty.

Copilot uses AI. Check for mistakes.

# aria2 has no socks support (see https://github.com/aria2/aria2/issues/153)
if echo "$http_proxy" | grep -q "^socks5h://" || echo "$https_proxy" | grep -q "^socks5h://"; then
msg "Socks proxy detected. Falling back to ${_APTMGR}" "hint"
"${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@"
CLEANUP_STATE=$?
exit
fi
Comment on lines 603 to 609
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the socks-proxy fallback block, CLEANUP_STATE=$?; exit relies on the EXIT trap to propagate the package manager’s exit code. For option="readonly", _create_lock isn’t called so no EXIT trap is installed, and exit will return the status of the last assignment (0), masking failures. Consider exiting explicitly with the captured status (e.g., exit "$CLEANUP_STATE") or skipping this socks-proxy fallback entirely for read-only commands.

Copilot uses AI. Check for mistakes.

Expand Down