web-dev-qa-db-ja.com

"。〜 / .bashrc"は、呼び出し元のスクリプトで期待どおりに変数を設定しません

背景

nodeを介してnvmをインストールするスクリプトを作成しました。

問題

install_latest_stable_node()内のコマンド_. $LOCAL_Shell_CONFIG_FILE_は実行されないため、nodeおよびnpmecho "$(which node)" && echo "$(which npm)")のPATHを出力すると、 2行の空白行を印刷します。

スクリプトが終了したら、_. ~/.bashrc_を実行してから、echo "$(which node)" && echo "$(which npm)"を実行します。これにより、今度は2行の空白行なしでPATHがエコーバックされます。

どうして?

コード

_#!/bin/bash

ask_for_Sudo() {

    # Ask for the administrator password upfront.

    Sudo -v &> /dev/null

    # Update existing `Sudo` time stamp
    # until this script has finished.
    #
    # https://Gist.github.com/cowboy/3118588

    while true; do
        Sudo -n true
        sleep 60
        kill -0 "$$" || exit
    done &> /dev/null &

}

show_spinner() {

    local -r FRAMES='/-\|'

    # shellcheck disable=SC2034
    local -r NUMBER_OR_FRAMES=${#FRAMES}

    local -r CMDS="$2"
    local -r MSG="$3"
    local -r PID="$1"

    local i=0
    local frameText=""

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Note: In order for the Travis CI site to display
    # things correctly, it needs special treatment, hence,
    # the "is Travis CI?" checks.

    if [ "$TRAVIS" != "true" ]; then

        # Provide more space so that the text hopefully
        # doesn't reach the bottom line of the terminal window.
        #
        # This is a workaround for escape sequences not tracking
        # the buffer position (accounting for scrolling).
        #
        # See also: https://unix.stackexchange.com/a/278888

        printf "\n\n\n"
        tput cuu 3

        tput sc

    fi

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Display spinner while the commands are being executed.

    while kill -0 "$PID" &>/dev/null; do

        frameText="   [${FRAMES:i++%NUMBER_OR_FRAMES:1}] $MSG"

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        # Print frame text.

        if [ "$TRAVIS" != "true" ]; then
            printf "%s\n" "$frameText"
        else
            printf "%s" "$frameText"
        fi

        sleep 0.2

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        # Clear frame text.

        if [ "$TRAVIS" != "true" ]; then
            tput rc
        else
            printf "\r"
        fi

    done

}

execute() {

    local -r CMDS="$1"
    local -r MSG="$2"
    local -r TMP_FILE="$(mktemp /tmp/XXXXX)"

    local exitCode=0
    local cmdsPID=""

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # If the current process is ended,
    # also end all its subprocesses.

    set_trap "EXIT" "kill_all_subprocesses"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Execute commands in background

    eval "$CMDS" \
        &> /dev/null \
        2> "$TMP_FILE" &

    cmdsPID=$!

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Show a spinner if the commands
    # require more time to complete.

    show_spinner "$cmdsPID" "$CMDS" "$MSG"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Wait for the commands to no longer be executing
    # in the background, and then get their exit code.

    wait "$cmdsPID" &> /dev/null
    exitCode=$?

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    # Print output based on what happened.

    print_result $exitCode "$MSG"

    if [ $exitCode -ne 0 ]; then
        print_error_stream < "$TMP_FILE"
    fi

    rm -rf "$TMP_FILE"

    # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    return $exitCode

}

set_trap() {

    trap -p "$1" | grep "$2" &> /dev/null \
        || trap '$2' "$1"

}

print_error_stream() {
    while read -r line; do
        print_error "↳ ERROR: $line"
    done
}

print_error() {
    print_in_red "   [✖] $1 $2\n"
}

print_success() {
    print_in_green "   [✔] $1\n"
}

print_in_red() {
    print_in_color "$1" 1
}

print_in_green() {
    print_in_color "$1" 2
}

print_in_purple() {
    print_in_color "$1" 5
}


print_in_color() {
    printf "%b" \
        "$(tput setaf "$2" 2> /dev/null)" \
        "$1" \
        "$(tput sgr0 2> /dev/null)"
}

print_result() {

    if [ "$1" -eq 0 ]; then
        print_success "$2"
    else
        print_error "$2"
    fi

    return "$1"

}

fix_dpkg() {
    declare -a files=("/var/lib/dpkg/lock" "/var/cache/apt/archives/" "/var/cache/apt/archives/lock")

    for i in "${files[@]}"
    do
        # If there is a dpkg lock, then remove it.
        if [ -e "$i" ]; then
            Sudo rm -rf "$i" &> /dev/null
        fi
    done
}


install_package() {

    declare -r PACKAGE="$2"
    declare -r PACKAGE_READABLE_NAME="$1"

    if ! package_is_installed "$PACKAGE"; then
        fix_dpkg
        execute "Sudo apt-get install --allow-unauthenticated -qqy $PACKAGE" "$PACKAGE_READABLE_NAME"
        #                                      suppress output ─┘│
        #            assume "yes" as the answer to all prompts ──┘
    else
        print_success "$PACKAGE_READABLE_NAME"
    fi

}

package_is_installed() {
    dpkg -s "$1" &> /dev/null
}

install_nvm() {

    # Install `nvm` and add the necessary
    # configs in the local Shell config file.
    # One-liner:
    # wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | PROFILE=~/.bash.local bash && . ~/.bash.local

    execute \
        "wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | \
            PROFILE=$LOCAL_Shell_CONFIG_FILE NVM_DIR=$NVM_DIRECTORY bash" \
        "nvm (install)"
}

update_nvm() {

    execute \
        "cd $NVM_DIRECTORY \
            && git fetch --quiet Origin \
            && git checkout --quiet \$(git describe --abbrev=0 --tags) \
            && . $NVM_DIRECTORY/nvm.sh" \
        "nvm (upgrade)"

}

install_latest_stable_node() {

    # Install the latest stable version of Node
    # (this will also set it as the default).
    # One-liner:
    #. ~/.bash.local && nvm install node

    . $LOCAL_Shell_CONFIG_FILE

    execute \
        "source $LOCAL_Shell_CONFIG_FILE \
        && nvm install node" \
        "nvm (install latest Node)"

}

main() {
    ask_for_Sudo

    touch "$LOCAL_Shell_CONFIG_FILE"

    print_in_purple "\n   Dependencies\n\n"

    install_package "git" "git"

    print_in_purple "\n   nvm\n\n"

    if [ -d "$NVM_DIRECTORY" ]; then
        rm -rf "$NVM_DIRECTORY"
    fi

    install_nvm
    update_nvm

    install_latest_stable_node

    print_in_purple "\n   PATHs\n\n"

    echo "$(which node)"
    echo "$(which npm)"
}

declare -r LOCAL_Shell_CONFIG_FILE="$HOME/.bashrc"
declare -r NVM_DIRECTORY="$HOME/.nvm"

main
_
1
Nicholas Adamou

スクリプトis指定されたファイルを調達します。

予期されたパス名の代わりに2つの空白行が表示される理由は、スクリプトがバックグラウンドでコマンドを実行する「execute()」関数内から$LOCAL_Shell_CONFIG_FILEを取得するためです(つまり、サブシェル内)。

サブシェルは、親シェルの環境を変更できません。

3
RobertL