22#
33# Summary: Find virtual environments that use pyenv-managed versions.
44#
5- # Usage: pyenv users [directory]
5+ # Usage: pyenv users [-r|--raw] [directory]
6+ #
7+ # -r/--raw Raw output strings as "<version>:<venv-path>"
68#
79# Scans [directory] for virtual environments whose `python` commands
810# are symlinks back into a pyenv version. Default: current directory.
911
10- if [ -n " $1 " ]; then
11- DIR=" $1 "
12- else
12+ set -e
13+
14+ parse_options () {
15+ # Parse the command line options. Taken from `pyenv-virtualenv`
16+ OPTIONS=()
17+ ARGUMENTS=()
18+ local arg option index
19+
20+ for arg in " $@ " ; do
21+ if [ " ${arg: 0: 1} " = " -" ]; then
22+ if [ " ${arg: 1: 1} " = " -" ]; then
23+ OPTIONS[${# OPTIONS[*]} ]=" ${arg: 2} "
24+ else
25+ index=1
26+ while option=" ${arg: $index : 1} " ; do
27+ [ -n " $option " ] || break
28+ OPTIONS[${# OPTIONS[*]} ]=" $option "
29+ index=$(( index+ 1 ))
30+ done
31+ fi
32+ else
33+ ARGUMENTS[${# ARGUMENTS[*]} ]=" $arg "
34+ fi
35+ done
36+ }
37+
38+ unset RAW
39+ parse_options " $@ "
40+ for option in " ${OPTIONS[@]} " ; do
41+ case " $option " in
42+ " r" | " raw" )
43+ RAW=true
44+ ;;
45+ " h" | " help" )
46+ pyenv help users
47+ exit 0
48+ ;;
49+ esac
50+ done
51+
52+ if [[ " ${# ARGUMENTS[@]} " == 0 ]]; then
1353 DIR=" $PYENV_DIR "
54+ elif [[ " ${# ARGUMENTS[@]} " == 1 ]]; then
55+ DIR=" ${ARGUMENTS[0]} "
56+ else
57+ echo -e " \nToo many directory arguments.\n"
58+ pyenv help users
59+ exit 1
1460fi
1561
62+ # ----------------------------------------------------------------------------
63+ # Finished parsing the arguments. Begin the actual functionality.
64+
65+ # The `links` are the symlink pathnames, `versions` are pyenv version strings,
66+ # and `venvs` are venv pathnames. Using parallel arrays since arrays-of-arrays
67+ # are a pain in bash. Keeping versions and venvs separate avoids needing awk.
68+ declare -a links versions venvs
69+
70+ if [ -z " $PYENV_ROOT " ]; then
71+ PYENV_ROOT=$( pyenv root)
72+ fi
73+
74+ # Collect all symlinks named `python` that point into $PYENV_ROOT
1675cmd=" readlink -f '{}' | grep -q ${PYENV_ROOT} "
17- unset links i
76+ unset i
1877while IFS= read -r -d $' \0' file; do
1978 links[i++]=" $file "
2079done < <( find -H " $DIR " -name " python" -type l -exec sh -c " $cmd " \; -print0)
2180
22- # Exit if no relevant venvs were found
23- if [[ $i -eq 0 ]]; then
24- exit 0
25- fi
26-
27- # Use columnar output if convenient.
28- if [ -n " $( command -v column) " ]; then
29- output=" column -t -s ':'"
30- else
31- output=" cat"
32- fi
33-
34- # Regex to extract the pyenv version from the target string. The
35- # second group consumes the actual binary (`python`, `pypy3`, etc)
81+ # Turn each link into a (version, venv) string pair
3682regex=" ${PYENV_ROOT} /versions/(.+)/bin/(.+)"
37-
83+ unset i
3884for link in " ${links[@]} " ; do
39- # `$link` is the `python` symlink, and `$target` is its target.
4085 linkpath=$( realpath -s " $link " )
4186 target=$( readlink -f " $link " )
4287 [[ " $target " =~ $regex ]]
@@ -45,6 +90,21 @@ for link in "${links[@]}"; do
4590 if grep -v -q " $PYENV_ROOT " <<< " $linkpath" || \
4691 grep -q " $PYENV_ROOT /versions/$version /envs" <<< " $linkpath"
4792 then
48- echo " $version " :" ${link%/ bin/ python} "
93+ versions[i]=" $version "
94+ venvs[i++]=" ${link%/ bin/ python} "
95+ fi
96+ done
97+
98+ # Print each (version, venv) pair
99+ declare -i K=${# versions[@]} width=0 maxwidth=0
100+ for (( k= 0 ; k < K; k++ )) ; do
101+ width=${# versions[k]}
102+ if (( width > maxwidth )) ; then maxwidth=$width ; fi
103+ done
104+ for (( k= 0 ; k < K; k++ )) ; do
105+ if [ -z " $RAW " ]; then
106+ printf " %-*s %s\n" " $maxwidth " " ${versions[$k]} " " ${venvs[k]} "
107+ else
108+ echo " ${versions[$k]} " :" ${venvs[$k]} "
49109 fi
50- done | sort | $output
110+ done | sort
0 commit comments