Created
October 30, 2025 06:52
-
-
Save ngenieer/c4a60f92e325ac5a41eb4030ce3b096f to your computer and use it in GitHub Desktop.
Manage uvx-based Python aliases in your shell rc (bash/zsh)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # setup_uvx_alias.sh | |
| # Manage uvx-based Python aliases in your shell rc (bash/zsh). | |
| # | |
| # Usage: | |
| # ./setup_uvx_alias.sh # install with default Python 3.12 | |
| # ./setup_uvx_alias.sh --python 3.11 # install with a different default Python | |
| # ./setup_uvx_alias.sh --rcfile ~/.zshrc # force specific rc file | |
| # ./setup_uvx_alias.sh --uninstall # remove the aliases block | |
| # | |
| # Notes: | |
| # - Per-project version override: create a file named ".python-version" with a version (e.g., 3.11) | |
| # in any project directory; the aliases will use that version while in that directory. | |
| # - Safe: the script adds a clearly marked block; uninstall simply removes that block. | |
| # | |
| # Requirements: | |
| # - uv must be installed and on PATH (https://github.com/astral-sh/uv). | |
| MARK_BEGIN="# >>> UVX ALIASES (managed) >>>" | |
| MARK_END="# <<< UVX ALIASES (managed) <<<" | |
| DEFAULT_PY="3.12" | |
| RCFILE="" | |
| UNINSTALL="0" | |
| err() { printf "\033[31m%s\033[0m\n" "$*" 1>&2; } | |
| ok() { printf "\033[32m%s\033[0m\n" "$*"; } | |
| info(){ printf "\033[36m%s\033[0m\n" "$*"; } | |
| detect_shell_rc() { | |
| if [[ -n "${RCFILE}" ]]; then | |
| echo "$RCFILE" | |
| return | |
| fi | |
| # Prefer user's current shell; fall back to bash | |
| case "${SHELL:-}" in | |
| *zsh) echo "${HOME}/.zshrc" ;; | |
| *bash) echo "${HOME}/.bashrc" ;; | |
| *) | |
| # If unknown, choose .bashrc by default | |
| echo "${HOME}/.bashrc" | |
| ;; | |
| esac | |
| } | |
| has_uv() { | |
| if command -v uv >/dev/null 2>&1; then | |
| return 0 | |
| else | |
| return 1 | |
| fi | |
| } | |
| backup_file() { | |
| local file="$1" | |
| if [[ -f "$file" ]]; then | |
| local ts | |
| ts="$(date +%Y%m%d-%H%M%S)" | |
| cp "$file" "${file}.bak-${ts}" | |
| info "Backup created: ${file}.bak-${ts}" | |
| fi | |
| } | |
| remove_block() { | |
| local file="$1" | |
| if [[ ! -f "$file" ]]; then | |
| info "RC file not found, nothing to remove: $file" | |
| return 0 | |
| fi | |
| if grep -Fq "$MARK_BEGIN" "$file"; then | |
| # Use awk to remove the managed block | |
| awk -v begin="$MARK_BEGIN" -v end="$MARK_END" ' | |
| BEGIN { inblock=0 } | |
| { | |
| if ($0 ~ begin) { inblock=1; next } | |
| if ($0 ~ end) { inblock=0; next } | |
| if (!inblock) print $0 | |
| } | |
| ' "$file" > "${file}.tmp" | |
| mv "${file}.tmp" "$file" | |
| ok "Removed uvx aliases block from: $file" | |
| else | |
| info "No managed block found in: $file" | |
| fi | |
| } | |
| install_block() { | |
| local file="$1" | |
| local pyver="$2" | |
| # Prepare the block content | |
| read -r -d '' BLOCK <<EOF | |
| ${MARK_BEGIN} | |
| # Default Python version for uvx-based aliases | |
| export UVX_DEFAULT_PYTHON="${pyver}" | |
| # Resolve effective version: use ./.python-version if present, else UVX_DEFAULT_PYTHON | |
| __uvx_resolve_ver() { | |
| local v | |
| if [[ -f ".python-version" ]]; then | |
| v="\$(cat .python-version | tr -d '[:space:]')" | |
| [[ -n "\$v" ]] || v="\${UVX_DEFAULT_PYTHON}" | |
| else | |
| v="\${UVX_DEFAULT_PYTHON}" | |
| fi | |
| printf "%s" "\$v" | |
| } | |
| # Aliases implemented as shell functions so we can pass arguments | |
| __uvx_python() { | |
| local ver="\$(__uvx_resolve_ver)" | |
| command uvx --python "\$ver" python "\$@" | |
| } | |
| __uvx_pip() { | |
| local ver="\$(__uvx_resolve_ver)" | |
| command uvx --python "\$ver" pip "\$@" | |
| } | |
| __uvx_ipy() { | |
| local ver="\$(__uvx_resolve_ver)" | |
| command uvx --python "\$ver" --with ipython ipython "\$@" | |
| } | |
| __uvx_jlab() { | |
| local ver="\$(__uvx_resolve_ver)" | |
| command uvx --python "\$ver" --with jupyterlab jupyter lab "\$@" | |
| } | |
| alias python="__uvx_python" | |
| alias pip="__uvx_pip" | |
| alias ipy="__uvx_ipy" | |
| alias jlab="__uvx_jlab" | |
| alias uvcache='ls "\$HOME/.cache/uv/python" 2>/dev/null || echo "No uv python cache found."' | |
| ${MARK_END} | |
| EOF | |
| # Ensure file exists | |
| touch "$file" | |
| # Remove old block if exists, then append new one | |
| if grep -Fq "$MARK_BEGIN" "$file"; then | |
| info "Existing managed block detected; replacing it." | |
| remove_block "$file" | |
| fi | |
| backup_file "$file" | |
| { | |
| echo "" | |
| echo "$BLOCK" | |
| echo "" | |
| } >> "$file" | |
| ok "Installed uvx aliases into: $file" | |
| } | |
| print_post_install() { | |
| local file="$1" | |
| cat <<EOM | |
| Next steps: | |
| 1) Reload your shell config: | |
| source "$file" | |
| # 또는 터미널을 새로 열기 | |
| 2) Test: | |
| python -V # should run via uvx (e.g., Python ${DEFAULT_PY}.x) | |
| pip --version # via uvx | |
| ipy # IPython via uvx | |
| jlab # JupyterLab via uvx | |
| 3) Per-project version: | |
| echo "3.11" > .python-version | |
| python -V # now uses 3.11 in this dir | |
| Uninstall later: | |
| ./setup_uvx_alias.sh --uninstall${RCFILE:+ --rcfile "$RCFILE"} | |
| EOM | |
| } | |
| # ---------- Parse args ---------- | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --python) | |
| shift | |
| [[ $# -gt 0 ]] || { err "--python requires a value (e.g., 3.12)"; exit 1; } | |
| DEFAULT_PY="$1" | |
| ;; | |
| --rcfile) | |
| shift | |
| [[ $# -gt 0 ]] || { err "--rcfile requires a path value"; exit 1; } | |
| RCFILE="$1" | |
| ;; | |
| --uninstall) | |
| UNINSTALL="1" | |
| ;; | |
| -h|--help) | |
| sed -n '1,120p' "$0" | |
| exit 0 | |
| ;; | |
| *) | |
| err "Unknown argument: $1" | |
| exit 1 | |
| ;; | |
| esac | |
| shift | |
| done | |
| RC_PATH="$(detect_shell_rc)" | |
| if [[ "$UNINSTALL" == "1" ]]; then | |
| info "Uninstalling uvx aliases from: $RC_PATH" | |
| remove_block "$RC_PATH" | |
| ok "Done." | |
| exit 0 | |
| fi | |
| if ! has_uv; then | |
| err "uv is not on PATH. Please install uv first: https://github.com/astral-sh/uv" | |
| exit 1 | |
| fi | |
| info "Installing uvx aliases into: $RC_PATH" | |
| install_block "$RC_PATH" "$DEFAULT_PY" | |
| print_post_install "$RC_PATH" |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
1) Check permissions to execute downloaded scripts (already set)
chmod +x setup_uvx_alias.sh
2) Install with default (Python 3.12)
./setup_uvx_alias.sh
3) If you want to use zsh or specify a specific rc file
./setup_uvx_alias.sh --rcfile ~/.zshrc
4) If you want to change the default Python version
./setup_uvx_alias.sh --python 3.11
./setup_uvx_alias.sh --uninstall
Apply immediately after installation
source ~/.bashrc # 또는 zsh면 source ~/.zshrc
python -V # uvx 기반 Python 실행
pip --version # uvx 환경 pip
ipy # IPython
jlab # JupyterLab