Bash

‘.~/.bashrc’ 沒有在呼叫腳本中按預期設置變數

  • August 25, 2017

背景

我已經編寫了一個腳本來node通過nvm.

問題

. $LOCAL_SHELL_CONFIG_FILE裡面的命令install_latest_stable_node()不會執行,因此,當它列印nodenpm( echo "$(which node)" && echo "$(which npm)") 的 PATH 時,它會列印兩個空行。

一旦腳本終止,我就可以執行. ~/.bashrc,然後執行,echo "$(which node)" && echo "$(which npm)"這將回顯 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

您的腳本正在獲取指定的文件。

您看到兩個空行而不是預期的路徑名的原因是腳本$LOCAL_SHELL_CONFIG_FILE從您的“”函式內部獲取原始碼execute(),該函式在後台(即在子外殼中)執行命令。

子shell 不能修改父shell 的環境。

引用自:https://unix.stackexchange.com/questions/388161