├── LICENSE ├── README.md ├── VERSION ├── fasttest └── speedtest /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Adriano http://rsvp.github.com 2 | All rights reserved. 3 | 4 | BSD LICENSE 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of speedtest-linux nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # speedtest-linux 2 | 3 | From the command line get ping/download/upload stats: 4 | 5 | - from https://speedtest.net or https://fast.com 6 | - *without* their ads 7 | - *without* their web GUI or app interface 8 | - simply **timestamped** in one-line CSV format 9 | - suitable for logs 10 | - using **Bash** shell script 11 | - for all POSIX systems 12 | 13 | 14 | ``` 15 | ______________| speedtest : ping, download and upload speeds. 16 | Commandline using speedtest.net resources. 17 | This standalone script is a wrapper. 18 | 19 | Usage: speedtest [--log|--simple|--verbose|--list|--fav|--version] 20 | 21 | Takes about a minute for results to appear. 22 | For logfile, the variable $logf can be modified. 23 | 24 | Examples: $ speedtest --simple 25 | Ping: 22.602 ms 26 | Download: 0.62 Mbit/s 27 | Upload: 0.25 Mbit/s 28 | 29 | $ speedtest # No args for single line timestamped. 30 | 2015-03-13, 19:25, 22.602, 0.62, 0.25 31 | 32 | $ speedtest --log # Will cat logfile with latest result. 33 | 34 | Dependencies: curl (Used to download the following Python script:) 35 | speedtest.py (https://github.com/sivel/speedtest-cli) 36 | ``` 37 | 38 | 39 | ## fasttest 40 | 41 | Get just the download speed via fast.com from the command line, 42 | suitable for logs. The infrastructure is provided by Netflix 43 | to make sure ISPs are not throttling their streaming movies. 44 | 45 | 46 | ``` 47 | ______________| fasttest : download speed in Mbps, flag to log results. 48 | Uses Netflix's fast.com resources, 49 | checking via both IPv4 and IPv6. 50 | This standalone script is a wrapper. 51 | 52 | Usage: fasttest [--log|--verbose] 53 | 54 | Takes about a minute for results to appear. 55 | For logfile, directory variable $logdir should be modified. 56 | 57 | Examples: $ fasttest # No args for single line timestamped. 58 | 2017-03-06, 19:25, None, 0.62, None 59 | 60 | $ fasttest --log # Will cat logfile with latest result. 61 | 62 | $ fasttest --log tmp.log # else default: fasttest.log 63 | 64 | Dependencies: curl (Used to download the following Python script:) 65 | fast_com.py (https://github.com/sanderjo/fast.com) 66 | ``` 67 | 68 | 69 | ## Comparison 70 | 71 | For execution time, speedtest runs about 60% faster than fasttest. 72 | In terms of information, speedtest provides more than fasttest, 73 | and is more accurate in measuring download speed. 74 | The code base for speedtest is more mature and receives more 75 | support and scrutiny, compared to that of fasttest. 76 | 77 | 78 | ## Magic one-liner 79 | 80 | Given our preferred choice: **speedtest** -- here is a way to directly 81 | access its service without cloning our repository: 82 | 83 | ``` 84 | $ echo "$(curl -skLO https://git.io/speedtest.sh && chmod +x speedtest.sh && ./speedtest.sh)" 85 | 2015-03-13, 19:25, 22.602, 0.62, 0.25 86 | ``` 87 | 88 | The curl command retrieves the most current version of speedtest, 89 | renamed for this exercise as *speedtest.sh*, then we execute it to 90 | display: *date*, local *time*, *ping* in milliseconds, followed by 91 | *download* and *upload* speeds in Mbps (Megabits per second). 92 | 93 | To fine-tune the logging functions, please fork our repository. 94 | 95 | 96 | ## Acknowledgements 97 | 98 | Many thanks to the developers upstream: @sivel and @sanderjo -- 99 | we rely on their latest updates to the Python source code. 100 | 101 | Shortcut to this project: https://git.io/speed 102 | 103 | 104 | --- 105 | 106 | README.md update : 2017-03-09 107 | 108 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v2.17.0306 2 | -------------------------------------------------------------------------------- /fasttest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bash 4.3.11(1) Linux Ubuntu 14.04.1 Date : 2017-03-07 3 | # 4 | # _______________| fasttest : download speed in Mbps, flag to log results. 5 | # Uses Netflix's fast.com resources, 6 | # checking via both IPv4 and IPv6. 7 | # This standalone script is a wrapper. 8 | # 9 | # Usage: fasttest [--log|--verbose] 10 | # 11 | # Takes about a minute for results to appear. 12 | # For logfile, directory variable $logdir should be modified. 13 | # 14 | # Examples: $ fasttest # No args for single line timestamped. 15 | # 2017-03-06, 19:25, None, 0.62, None 16 | # 17 | # $ fasttest --log # Will cat logfile with latest result. 18 | # 19 | # $ fasttest --log tmp.log # else default: fasttest.log 20 | # 21 | # Dependencies: curl [ Used to download the following Python script: ] 22 | # fast_com.py ( https://github.com/sanderjo/fast.com ) 23 | # python2 (Our script should work under bash regardless.) 24 | # grep, awk 25 | # 26 | # CHANGE LOG ORIGIN: https://github.com/rsvp/speedtest-linux 27 | # 2017-03-07 Use maximum from verbose results, not the average 28 | # (though it's still available using --average flag). 29 | # Change log format to match that of speedtest, 30 | # thus "None" for both ping and upload stats. 31 | # 2017-03-06 First version uses speedtest 2016-01-18 as template. 32 | # This script ALWAYS retrieves the latest dependent code. 33 | # Currently it's ded23e4 on Jun 4, 2016 from primary source. 34 | 35 | 36 | # _____ PREAMBLE_v3: settings, variables, and error handling. 37 | # 38 | LC_ALL=POSIX 39 | # locale means "ASCII, US English, no special rules, 40 | # output per ISO and RFC standards." 41 | # Esp. use ASCII encoding for glob and sorting characters. 42 | shopt -s extglob 43 | # ^set extended glob for pattern matching. 44 | shopt -s failglob 45 | # ^failed pattern matching signals error. 46 | set -e 47 | # ^errors checked: immediate exit if a command has non-zero status. 48 | set -o pipefail 49 | # ^exit status on fail within pipe, not (default) last command. 50 | set -u 51 | # ^unassigned variables shall be errors. 52 | # Example of default VARIABLE ASSIGNMENT: arg1=${1:-'foo'} 53 | 54 | arg1=${1:-'NULL'} 55 | arg2=${2:-'fasttest.log'} 56 | # ^default name for logfile, see $logf below. 57 | 58 | program=${0##*/} # similar to using basename 59 | memf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX ) 60 | mem2=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX ) 61 | errf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX ) 62 | 63 | 64 | cleanup () { 65 | # Delete temporary files, then optionally exit given status. 66 | local status=${1:-'0'} 67 | rm -f $memf $mem2 $errf 68 | [ $status = '-1' ] || exit $status # thus -1 prevents exit. 69 | } #-------------------------------------------------------------------- 70 | warn () { 71 | # Message with basename to stderr. Usage: warn "message" 72 | echo -e "\n !! ${program}: $1 " >&2 73 | } #-------------------------------------------------------------------- 74 | die () { 75 | # Exit with status of most recent command or custom status, after 76 | # cleanup and warn. Usage: command || die "message" [status] 77 | local status=${2:-"$?"} 78 | cat $errf >&2 79 | cleanup -1 && warn "$1" && exit $status 80 | } #-------------------------------------------------------------------- 81 | trap "die 'SIG disruption: but finish needs about one minute.' 114" 1 2 3 15 82 | # Cleanup after INTERRUPT: 1=SIGHUP, 2=SIGINT, 3=SIGQUIT, 15=SIGTERM 83 | trap "die 'unhandled ERR via trap, but cleanup finished.' 116" ERR 84 | # Cleanup after command failure unless it's part of a test clause. 85 | # 86 | # _______________ :: BEGIN Script :::::::::::::::::::::::::::::::::::::::: 87 | 88 | 89 | logdir="$HOME/var/log/net" 90 | # ^RENAME log directory for your personal use; for --log option. 91 | 92 | [ -d "$logdir" ] || mkdir -p "$logdir" || die "fail creating $logdir" 117 93 | # Check directory's existence, else create it. 94 | 95 | logf="$logdir/$arg2" 96 | # Suggested first line HEADER for CSV: Date, Time, DownMbps 97 | 98 | 99 | # Relying on a STATIC LOCAL VERSION may get outdated, so 100 | # DOWNLOAD and execute LATEST PRIMARY VERSION of speedtest-cli.py: 101 | source='https://raw.githubusercontent.com/sanderjo/fast.com/master/fast_com.py' 102 | # If @sanderjo disappears for any reason, try mirror @rsvp. 103 | # 104 | # Retrieve source and place it in memory with execute permission: 105 | curl -kL --silent $source > $memf \ 106 | || die "curl unable to retrieve $program source code." 113 107 | chmod 755 $memf 108 | # ___ATTN___ Their header is "#!/usr/bin/env python" 109 | # but the code is only python2 compatible, so use fast_verbose: 110 | 111 | 112 | fast_verbose () { 113 | # Get the verbose output for "without logging", IPv4, and IPv6 114 | python2 $memf 2> $errf 115 | # Primary code base is not python3 compatible. 116 | } 117 | 118 | 119 | fast_average () { 120 | # Use awk to average the numbers from verbose (in Mbps): 121 | fast_verbose | grep '^[0-9]' > $mem2 122 | # ... only reasonable way to parse verbose: save just the numbers. 123 | awk -f - $mem2 < "/dev/stderr" } 129 | EOHereDoc 130 | } 131 | 132 | # 2017-03-07 The flag --verbose will show all protocols being tested. 133 | # Some may indicate 0 Mbps depending on the user's situation. 134 | # In that case, the average will be unnecessarily dragged 135 | # lower, so the MAXIMUM among protocols will be our 136 | # default metric. (The flag --average is available though.) 137 | 138 | 139 | fast_maximum () { 140 | # Use awk to find maximum from verbose (in Mbps): 141 | fast_verbose | grep '^[0-9]' > $mem2 142 | # ... only reasonable way to parse verbose: save just the numbers. 143 | awk -f - $mem2 < max) { max = \$1 }} 146 | END { if (NR !=0) 147 | { print max } 148 | else 149 | print " :: fasttest FAIL: nothing to maximize." > "/dev/stderr" } 150 | EOHereDoc 151 | } 152 | 153 | 154 | timestamped () { 155 | # Get time, then put into CSV format: 156 | epoch="$( date '+%Y-%m-%d, %H:%M' )" 157 | speeds="$( fast_maximum )" 158 | # ^just a single real number: download speed in Mbps 159 | echo "$epoch, None, $speeds, None" 160 | # ^compatible with speedtest format: ping, download, upload. 161 | # Sample result: "2017-03-06, 19:25, None, 0.62, None" 162 | } 163 | 164 | 165 | 166 | case "$arg1" in 167 | NULL) timestamped ;; 168 | --log) timestamped >> "$logf" && cat "$logf" ;; 169 | 170 | --verbose) fast_verbose ;; 171 | --average) fast_average ;; 172 | 173 | --default) echo "Default log directory: $logdir " 174 | echo "Default log file: $arg2 " ;; 175 | 176 | *) die "undefined arg: $arg1" 115 ;; 177 | esac 178 | 179 | 180 | 181 | cleanup # Instead of: trap arg EXIT 182 | # _______________ EOS :: END of Script :::::::::::::::::::::::::::::::::::::::: 183 | 184 | # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=sh : 185 | -------------------------------------------------------------------------------- /speedtest: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # bash 4.3.11(1) Linux Ubuntu 14.04.1 Date : 2017-03-06 3 | # 4 | # _______________| speedtest : ping, download and upload speeds. 5 | # Commandline using speedtest.net resources. 6 | # This standalone script is a wrapper. 7 | # 8 | # Usage: speedtest [--log|--simple|--verbose|--list|--fav|--version] 9 | # 10 | # Takes about a minute for results to appear. 11 | # For logfile, directory variable $logdir should be modified. 12 | # 13 | # Examples: $ speedtest --simple 14 | # Ping: 22.602 ms 15 | # Download: 0.62 Mbit/s 16 | # Upload: 0.25 Mbit/s 17 | # 18 | # $ speedtest # No args for single line timestamped. 19 | # 2015-03-13, 19:25, 22.602, 0.62, 0.25 20 | # 21 | # $ speedtest --log # Will cat logfile with latest result. 22 | # 23 | # $ speedtest --log tmp.log # else default: speedtest.log 24 | # 25 | # Dependencies: curl [ Used to download the following Python script: ] 26 | # speedtest.py ( https://github.com/sivel/speedtest-cli ) 27 | # 28 | # CHANGE LOG ORIGIN: https://github.com/rsvp/speedtest-linux 29 | # 2017-03-06 Upstream deprecates speedtest-cli.py in favor of speedtest.py 30 | # 2016-01-18 Fix issue #2 by using $HOME instead of /home/${USER} for logdir. 31 | # @oeolartep: $USER was unbound variable in cron jobs. 32 | # 2015-03-19 Second argument names log file. Directory's existence tested. 33 | # 2015-03-17 Clarify some comments. 34 | # 2015-03-13 This script ALWAYS retrieves the latest dependent code. 35 | # Currently it is version 0.3.2 from the primary source. 36 | 37 | 38 | # _____ PREAMBLE_v3: settings, variables, and error handling. 39 | # 40 | LC_ALL=POSIX 41 | # locale means "ASCII, US English, no special rules, 42 | # output per ISO and RFC standards." 43 | # Esp. use ASCII encoding for glob and sorting characters. 44 | shopt -s extglob 45 | # ^set extended glob for pattern matching. 46 | shopt -s failglob 47 | # ^failed pattern matching signals error. 48 | set -e 49 | # ^errors checked: immediate exit if a command has non-zero status. 50 | set -o pipefail 51 | # ^exit status on fail within pipe, not (default) last command. 52 | set -u 53 | # ^unassigned variables shall be errors. 54 | # Example of default VARIABLE ASSIGNMENT: arg1=${1:-'foo'} 55 | 56 | arg1=${1:-'NULL'} 57 | arg2=${2:-'speedtest.log'} 58 | # ^default name for logfile, see $logf below. 59 | 60 | program=${0##*/} # similar to using basename 61 | memf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX ) 62 | errf=$( mktemp /dev/shm/88_${program}_tmp.XXXXXXXXXX ) 63 | 64 | 65 | cleanup () { 66 | # Delete temporary files, then optionally exit given status. 67 | local status=${1:-'0'} 68 | rm -f $memf $errf 69 | [ $status = '-1' ] || exit $status # thus -1 prevents exit. 70 | } #-------------------------------------------------------------------- 71 | warn () { 72 | # Message with basename to stderr. Usage: warn "message" 73 | echo -e "\n !! ${program}: $1 " >&2 74 | } #-------------------------------------------------------------------- 75 | die () { 76 | # Exit with status of most recent command or custom status, after 77 | # cleanup and warn. Usage: command || die "message" [status] 78 | local status=${2:-"$?"} 79 | cat $errf >&2 80 | cleanup -1 && warn "$1" && exit $status 81 | } #-------------------------------------------------------------------- 82 | trap "die 'SIG disruption: but finish needs about one minute.' 114" 1 2 3 15 83 | # Cleanup after INTERRUPT: 1=SIGHUP, 2=SIGINT, 3=SIGQUIT, 15=SIGTERM 84 | trap "die 'unhandled ERR via trap, but cleanup finished.' 116" ERR 85 | # Cleanup after command failure unless it's part of a test clause. 86 | # 87 | # _______________ :: BEGIN Script :::::::::::::::::::::::::::::::::::::::: 88 | 89 | 90 | favorite='1335' 91 | # ^DreamHost in Los Angeles for --fav option. 92 | # Use --list to find your favorite. 93 | 94 | logdir="$HOME/var/log/net" 95 | # ^RENAME log directory for your personal use; for --log option. 96 | 97 | [ -d "$logdir" ] || mkdir -p "$logdir" || die "fail creating $logdir" 117 98 | # Check directory's existence, else create it. 99 | 100 | logf="$logdir/$arg2" 101 | # Suggested first line HEADER for CSV: Date, Time, Ping, Down, Up 102 | 103 | 104 | # Relying on a STATIC LOCAL VERSION may get outdated, so 105 | # DOWNLOAD and execute LATEST PRIMARY VERSION of speedtest-cli.py: 106 | source='https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py' 107 | # If @sivel disappears for any reason, try mirror @rsvp. 108 | # 109 | # Retrieve source and place it in memory with execute permission: 110 | curl -kL --silent $source > $memf \ 111 | || die "curl unable to retrieve $program source code." 113 112 | chmod 755 $memf 113 | 114 | 115 | timestamped () { 116 | # Get time, then --simple in one line, finally combine into CSV format. 117 | epoch="$( date '+%Y-%m-%d, %H:%M' )" 118 | speeds="$( echo $( $memf --simple | sed -e 's/^.*: //' -e 's/ .*s$/, /' ))" 119 | # ^on a single line: ping, download, upload speeds -- given output: 120 | # Ping: 22.602 ms 121 | # Download: 0.62 Mbit/s 122 | # Upload: 0.25 Mbit/s 123 | echo "$epoch, $speeds" | sed -e 's/,$//' 124 | # Sample result: "2015-03-13, 19:25, 22.602, 0.62, 0.25" 125 | } 126 | 127 | 128 | case "$arg1" in 129 | NULL) timestamped ;; 130 | --log) timestamped >> "$logf" && cat "$logf" ;; 131 | 132 | --simple) $memf --simple ;; 133 | # Not specifying favorite server lets the program 134 | # pick the server with lowest latency. 135 | --fav) $memf --simple --server $favorite ;; 136 | --verbose) $memf ;; 137 | --version) $memf --version || true ;; 138 | --list) $memf --list ;; 139 | # list of worldwide servers. 140 | 141 | --default) echo "Default log directory: $logdir " 142 | echo "Default log file: $arg2 " 143 | echo "--fav test server: $favorite" ;; 144 | 145 | *) die "undefined arg: $arg1" 115 ;; 146 | esac 147 | 148 | 149 | 150 | cleanup # Instead of: trap arg EXIT 151 | # _______________ EOS :: END of Script :::::::::::::::::::::::::::::::::::::::: 152 | 153 | # vim: set fileencoding=utf-8 ff=unix tw=78 ai syn=sh : 154 | --------------------------------------------------------------------------------