source命令用于在当前Shell环境中执行指定的脚本文件,而不是创建新的子Shell。这使得脚本中定义的变量、函数、别名等在当前Shell中立即生效,常用于加载配置文件和环境变量。
source命令的主要作用是在当前Shell环境中执行脚本:
| 执行方式 | 是否创建子Shell | 变量是否继承 |
|---|---|---|
source script.sh |
否 | 是(变量在当前Shell生效) |
bash script.sh |
是 | 否(变量在子Shell中) |
./script.sh |
是 | 否(需要执行权限) |
source 文件名 [参数]
# 或
. 文件名 [参数]
# 示例
source ~/.bashrc
source /etc/profile
source config.sh arg1 arg2
# 参数会传递给脚本
source script.sh arg1 arg2
# 在脚本中通过 $1, $2 等获取参数
# 点命令和source完全等价
. ~/.bashrc # 等同于 source ~/.bashrc
. /etc/profile # 等同于 source /etc/profile
# 带参数的点命令
. script.sh arg1 arg2
# 特殊用法:在当前目录查找文件
. ./config.sh # 执行当前目录下的config.sh
# 注意:点后面必须有空格
. file.sh # 正确
.file.sh # 错误(会被解释为隐藏文件)
# 1. 重新加载Shell配置文件(最常用)
source ~/.bashrc # 重新加载bash配置
source ~/.bash_profile # 重新加载bash_profile
source ~/.zshrc # 重新加载zsh配置
source ~/.profile # 重新加载profile
# 2. 加载环境变量文件
source /etc/environment
source ~/.env
source .env.local
# 3. 执行初始化脚本
source /etc/profile.d/*.sh # 加载所有profile.d脚本
source /usr/local/bin/init.sh
# 4. 加载函数库
source /usr/lib/bash/functions.sh
source ~/.bash_functions
# 5. 加载别名定义
source ~/.bash_aliases
# 6. 设置特定项目环境
source venv/bin/activate # Python虚拟环境
source /opt/ros/noetic/setup.bash # ROS环境
# 7. 执行配置脚本
source configure --prefix=/usr/local
# 8. 测试脚本而不创建子Shell
source test_script.sh
# 9. 从标准输入读取并执行
echo "export PATH=\$PATH:/usr/local/bin" | source /dev/stdin
# 10. 条件加载配置
if [ -f ~/.custom_env ]; then
source ~/.custom_env
fi
# 1. 创建环境变量配置文件
cat > ~/.myenv << 'EOF'
# 环境变量配置文件
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export PATH=$JAVA_HOME/bin:$PATH
export EDITOR=vim
export LANG=en_US.UTF-8
# 项目特定变量
export PROJECT_HOME=~/projects/myapp
export DATABASE_URL=postgres://localhost/mydb
# 别名
alias ll='ls -la'
alias gp='git pull'
# 函数
myfunc() {
echo "Hello from myfunc"
}
EOF
# 2. 加载环境变量文件
source ~/.myenv
# 3. 验证变量是否生效
echo $JAVA_HOME
echo $PATH
# 4. 动态更新PATH
# 创建一个添加路径的函数
add_to_path() {
if [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
fi
}
# 保存到文件
echo 'add_to_path() {
if [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
fi
}' > ~/.path_functions
# 加载函数
source ~/.path_functions
# 使用函数添加路径
add_to_path /usr/local/go/bin
add_to_path ~/bin
# 5. 卸载环境变量(从当前Shell移除)
# 创建一个unset_env.sh文件
cat > unset_env.sh << 'EOF'
unset JAVA_HOME
unset PROJECT_HOME
# 从PATH中移除特定目录
export PATH=$(echo $PATH | sed 's|:/usr/local/go/bin||')
EOF
# 执行卸载
source unset_env.sh
# 6. 环境变量继承演示
# script.sh内容:
# echo "In script: VAR=$VAR"
# export VAR="script_value"
# 创建测试脚本
cat > test_env.sh << 'EOF'
#!/bin/bash
echo "In script: VAR=$VAR"
export VAR="script_value"
EOF
# 测试1:使用bash执行(不继承)
VAR="parent_value"
bash test_env.sh # 输出: In script: VAR=
echo $VAR # 输出: parent_value
# 测试2:使用source执行(继承)
VAR="parent_value"
source test_env.sh # 输出: In script: VAR=parent_value
echo $VAR # 输出: script_value
# 1. 创建函数库文件
cat > ~/.bash_functions << 'EOF'
# 系统信息函数
sysinfo() {
echo "=== 系统信息 ==="
echo "主机名: $(hostname)"
echo "内核: $(uname -r)"
echo "内存: $(free -h | awk '/^Mem:/ {print $3 "/" $2}')"
echo "磁盘: $(df -h / | awk 'NR==2 {print $4 "/" $2 "可用"}')"
}
# Git快捷函数
gcm() {
git commit -m "$1"
}
gacp() {
git add .
git commit -m "$1"
git push
}
# 文件操作函数
mkcd() {
mkdir -p "$1"
cd "$1"
}
# 网络函数
myip() {
curl -s ifconfig.me
echo
}
# 工具函数
# 提取压缩文件
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "'$1' 无法被提取" ;;
esac
else
echo "'$1' 不是一个有效的文件"
fi
}
EOF
# 2. 加载函数库
source ~/.bash_functions
# 3. 使用函数
sysinfo
mkcd new_directory
extract archive.tar.gz
# 4. 查看已定义的函数
declare -f sysinfo # 查看特定函数定义
declare -F # 查看所有函数名
typeset -f # 查看所有函数定义
# 5. 函数覆盖和重载
# 如果函数已存在,重新加载会覆盖
cat > new_functions.sh << 'EOF'
sysinfo() {
echo "简洁版系统信息:"
echo "主机: $(hostname)"
echo "用户: $(whoami)"
}
EOF
source new_functions.sh
sysinfo # 现在使用新版本
# 6. 删除函数
unset -f sysinfo # 删除sysinfo函数
declare -F # 确认函数已删除
# 7. 条件加载函数
if [ -f ~/.bash_functions ]; then
source ~/.bash_functions
else
echo "函数库文件不存在"
fi
# 8. 函数参数传递
cat > func_with_args.sh << 'EOF'
greet() {
local name=${1:-"World"}
local greeting=${2:-"Hello"}
echo "$greeting, $name!"
}
EOF
source func_with_args.sh
greet # 输出: Hello, World!
greet "Alice" # 输出: Hello, Alice!
greet "Bob" "Hi" # 输出: Hi, Bob!
# 1. 创建配置文件管理脚本
cat > ~/reload_configs.sh << 'EOF'
#!/bin/bash
# 配置文件重载脚本
reload_bash() {
echo "重新加载 bash 配置..."
if [ -f ~/.bash_profile ]; then
source ~/.bash_profile
elif [ -f ~/.bash_login ]; then
source ~/.bash_login
elif [ -f ~/.profile ]; then
source ~/.profile
fi
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
echo "bash 配置已重载"
}
reload_zsh() {
echo "重新加载 zsh 配置..."
if [ -f ~/.zshrc ]; then
source ~/.zshrc
fi
echo "zsh 配置已重载"
}
reload_all() {
case "$SHELL" in
*/bash)
reload_bash
;;
*/zsh)
reload_zsh
;;
*)
echo "未知的shell: $SHELL"
;;
esac
# 加载通用配置
if [ -f ~/.aliases ]; then
source ~/.aliases
fi
if [ -f ~/.functions ]; then
source ~/.functions
fi
if [ -f ~/.env ]; then
source ~/.env
fi
}
# 根据参数执行
case "$1" in
bash)
reload_bash
;;
zsh)
reload_zsh
;;
all|"")
reload_all
;;
*)
echo "用法: $0 {bash|zsh|all}"
;;
esac
EOF
chmod +x ~/reload_configs.sh
# 2. 使用重载脚本
~/reload_configs.sh # 重载所有配置
~/reload_configs.sh bash # 只重载bash配置
~/reload_configs.sh zsh # 只重载zsh配置
# 3. 创建alias简化操作
echo "alias reload='source ~/.bashrc'" >> ~/.bashrc
echo "alias rl='reload'" >> ~/.bashrc
source ~/.bashrc
# 现在可以直接使用
reload # 重新加载bash配置
rl # 简写
# 4. 自动重载配置(当文件变化时)
# 使用inotifywait监控文件变化
# 安装inotify-tools: sudo apt-get install inotify-tools
cat > ~/auto_reload.sh << 'EOF'
#!/bin/bash
# 自动重载配置文件
CONFIG_FILES=(
"$HOME/.bashrc"
"$HOME/.bash_aliases"
"$HOME/.bash_functions"
"$HOME/.profile"
)
echo "开始监控配置文件变化..."
while true; do
# 等待任何配置文件变化
inotifywait -e modify "${CONFIG_FILES[@]}" 2>/dev/null
# 文件变化后重新加载
echo "检测到配置文件变化,重新加载..."
for file in "${CONFIG_FILES[@]}"; do
if [ -f "$file" ]; then
echo "加载: $file"
source "$file"
fi
done
echo "配置文件已重载"
done
EOF
chmod +x ~/auto_reload.sh
# 5. 安全重载(避免循环加载)
cat > ~/.safe_reload.sh << 'EOF'
#!/bin/bash
# 安全重载配置,避免循环
# 检查是否已经在重载中
if [ -n "$RELOADING" ]; then
echo "已经在重载过程中,跳过..."
return 0
fi
export RELOADING=1
# 要重载的文件列表
FILES=(
~/.bashrc
~/.bash_aliases
~/.bash_functions
~/.profile
)
for file in "${FILES[@]}"; do
if [ -f "$file" ]; then
echo "加载: $file"
# 使用点命令而不是source,避免某些shell的兼容性问题
. "$file"
fi
done
unset RELOADING
echo "安全重载完成"
EOF
# 6. 测试配置文件是否有效
cat > ~/test_config.sh << 'EOF'
#!/bin/bash
# 测试配置文件语法
TEST_FILES=("$@")
for file in "${TEST_FILES[@]}"; do
if [ ! -f "$file" ]; then
echo "错误: 文件不存在 - $file"
continue
fi
echo "测试: $file"
# 使用bash -n检查语法
if bash -n "$file"; then
echo "✓ 语法正确"
# 尝试加载测试
if source "$file" 2>/dev/null; then
echo "✓ 加载成功"
else
echo "✗ 加载失败"
fi
else
echo "✗ 语法错误"
fi
echo "---"
done
EOF
chmod +x ~/test_config.sh
~/test_config.sh ~/.bashrc ~/.bash_aliases
#!/bin/bash
# 项目环境配置管理器
# 文件名:project_env_manager.sh
PROJECTS_DIR="$HOME/projects"
ENV_DIR="$HOME/.project_envs"
# 初始化
init_manager() {
mkdir -p "$PROJECTS_DIR"
mkdir -p "$ENV_DIR"
echo "项目环境管理器初始化完成"
}
# 创建项目环境
create_project_env() {
local project_name="$1"
local project_path="$PROJECTS_DIR/$project_name"
local env_file="$ENV_DIR/$project_name.env"
if [ -z "$project_name" ]; then
echo "使用方法: create_project_env 项目名"
return 1
fi
# 创建项目目录
mkdir -p "$project_path"
# 创建环境配置文件
cat > "$env_file" << EOF
# 项目: $project_name
# 创建时间: $(date)
# 环境配置文件
# 项目路径
export PROJECT_NAME="$project_name"
export PROJECT_PATH="$project_path"
# 添加到PATH
export PATH="\$PROJECT_PATH/bin:\$PATH"
# 项目特定变量
export PROJECT_ENV="development"
export LOG_LEVEL="INFO"
# 别名
alias pcd="cd \$PROJECT_PATH"
alias prun="cd \$PROJECT_PATH && make run"
alias ptest="cd \$PROJECT_PATH && make test"
# 函数
pstatus() {
echo "=== 项目状态 ==="
echo "名称: \$PROJECT_NAME"
echo "路径: \$PROJECT_PATH"
echo "环境: \$PROJECT_ENV"
echo "时间: \$(date)"
}
# 加载项目特定配置
if [ -f "\$PROJECT_PATH/.env.local" ]; then
source "\$PROJECT_PATH/.env.local"
fi
EOF
echo "项目 '$project_name' 环境已创建"
echo "环境文件: $env_file"
echo "项目目录: $project_path"
}
# 激活项目环境
activate_project() {
local project_name="$1"
local env_file="$ENV_DIR/$project_name.env"
if [ -z "$project_name" ]; then
echo "可用项目:"
ls "$ENV_DIR"/*.env 2>/dev/null | xargs -n1 basename | sed 's/\.env$//'
return 0
fi
if [ ! -f "$env_file" ]; then
echo "错误: 项目 '$project_name' 不存在"
return 1
fi
# 保存当前环境
if [ -n "$CURRENT_PROJECT" ]; then
deactivate_project
fi
# 激活新项目环境
echo "激活项目: $project_name"
source "$env_file"
export CURRENT_PROJECT="$project_name"
# 进入项目目录
cd "$PROJECT_PATH" 2>/dev/null || true
echo "项目环境已激活"
pstatus
}
# 停用项目环境
deactivate_project() {
if [ -z "$CURRENT_PROJECT" ]; then
echo "没有激活的项目"
return 0
fi
local project_name="$CURRENT_PROJECT"
local env_file="$ENV_DIR/$project_name.env"
# 从env文件获取要unset的变量
if [ -f "$env_file" ]; then
# 提取export语句中的变量名
local vars_to_unset=$(grep '^export' "$env_file" | \
sed 's/^export //' | \
cut -d= -f1 | \
tr '\n' ' ')
# 取消设置变量
for var in $vars_to_unset; do
unset "$var" 2>/dev/null || true
done
fi
# 从PATH中移除项目路径
export PATH=$(echo "$PATH" | sed "s|$PROJECTS_DIR/[^:]*/bin:||g")
unset CURRENT_PROJECT
echo "项目 '$project_name' 环境已停用"
}
# 列出所有项目
list_projects() {
echo "=== 项目列表 ==="
if [ ! -d "$ENV_DIR" ] || [ -z "$(ls -A "$ENV_DIR")" ]; then
echo "暂无项目"
return 0
fi
for env_file in "$ENV_DIR"/*.env; do
local project_name=$(basename "$env_file" .env)
local project_path="$PROJECTS_DIR/$project_name"
local is_active=""
if [ "$project_name" = "$CURRENT_PROJECT" ]; then
is_active=" (激活中)"
fi
echo "项目: $project_name$is_active"
echo " 环境文件: $env_file"
echo " 项目目录: $project_path"
if [ -d "$project_path" ]; then
echo " 目录状态: 存在"
else
echo " 目录状态: 不存在"
fi
echo ""
done
}
# 删除项目环境
delete_project_env() {
local project_name="$1"
if [ -z "$project_name" ]; then
echo "使用方法: delete_project_env 项目名"
return 1
fi
# 如果正在激活,先停用
if [ "$project_name" = "$CURRENT_PROJECT" ]; then
deactivate_project
fi
local env_file="$ENV_DIR/$project_name.env"
local project_path="$PROJECTS_DIR/$project_name"
# 确认删除
echo "即将删除:"
echo " 环境文件: $env_file"
echo " 项目目录: $project_path"
echo -n "确认删除?(y/N): "
read -r confirm
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
rm -f "$env_file"
echo "环境文件已删除"
echo -n "是否同时删除项目目录?(y/N): "
read -r confirm_dir
if [ "$confirm_dir" = "y" ] || [ "$confirm_dir" = "Y" ]; then
rm -rf "$project_path"
echo "项目目录已删除"
fi
echo "项目 '$project_name' 已删除"
else
echo "取消删除"
fi
}
# 主菜单
main_menu() {
init_manager
while true; do
echo ""
echo "=== 项目环境管理器 ==="
echo "1. 创建新项目环境"
echo "2. 激活项目环境"
echo "3. 停用当前项目"
echo "4. 列出所有项目"
echo "5. 删除项目环境"
echo "6. 显示当前项目"
echo "7. 退出"
echo -n "请选择 [1-7]: "
read -r choice
case $choice in
1)
echo -n "请输入项目名: "
read -r project_name
create_project_env "$project_name"
;;
2)
echo -n "请输入要激活的项目名(留空查看列表): "
read -r project_name
activate_project "$project_name"
;;
3)
deactivate_project
;;
4)
list_projects
;;
5)
echo -n "请输入要删除的项目名: "
read -r project_name
delete_project_env "$project_name"
;;
6)
if [ -n "$CURRENT_PROJECT" ]; then
echo "当前激活项目: $CURRENT_PROJECT"
pstatus 2>/dev/null || echo "无法显示项目状态"
else
echo "没有激活的项目"
fi
;;
7)
echo "退出"
exit 0
;;
*)
echo "无效选择"
;;
esac
done
}
# 运行主菜单
main_menu
#!/bin/bash
# 安全的环境变量加载器
# 文件名:secure_env_loader.sh
ENV_DIR="$HOME/.secure_envs"
KEY_FILE="$HOME/.env_key"
LOG_FILE="/var/log/env_loader.log"
# 初始化
init_loader() {
mkdir -p "$ENV_DIR"
touch "$LOG_FILE"
# 生成加密密钥(如果不存在)
if [ ! -f "$KEY_FILE" ]; then
openssl rand -base64 32 > "$KEY_FILE"
chmod 600 "$KEY_FILE"
echo "已生成新的加密密钥"
fi
echo "安全环境加载器初始化完成"
}
# 记录日志
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
}
# 加密环境变量文件
encrypt_env() {
local env_name="$1"
local env_file="$ENV_DIR/$env_name.env"
local enc_file="$ENV_DIR/$env_name.env.enc"
if [ -z "$env_name" ]; then
echo "使用方法: encrypt_env 环境名"
return 1
fi
if [ ! -f "$env_file" ]; then
echo "错误: 环境文件不存在 - $env_file"
return 1
fi
# 加密文件
openssl enc -aes-256-cbc -salt -in "$env_file" -out "$enc_file" -pass file:"$KEY_FILE"
if [ $? -eq 0 ]; then
# 删除明文文件
rm -f "$env_file"
echo "环境 '$env_name' 已加密"
log_message "INFO" "加密环境: $env_name"
else
echo "加密失败"
log_message "ERROR" "加密失败: $env_name"
fi
}
# 解密并加载环境变量
load_env() {
local env_name="$1"
local enc_file="$ENV_DIR/$env_name.env.enc"
local temp_file="/tmp/$env_name.env.$$"
if [ -z "$env_name" ]; then
echo "可用环境:"
ls "$ENV_DIR"/*.env.enc 2>/dev/null | xargs -n1 basename | sed 's/\.env\.enc$//'
return 0
fi
if [ ! -f "$enc_file" ]; then
echo "错误: 加密环境文件不存在 - $enc_file"
return 1
fi
# 解密到临时文件
openssl enc -aes-256-cbc -d -in "$enc_file" -out "$temp_file" -pass file:"$KEY_FILE" 2>/dev/null
if [ $? -ne 0 ]; then
echo "解密失败,请检查密钥"
log_message "ERROR" "解密失败: $env_name"
rm -f "$temp_file"
return 1
fi
# 检查文件语法
if ! bash -n "$temp_file" 2>/dev/null; then
echo "错误: 环境文件语法错误"
log_message "ERROR" "语法错误: $env_name"
rm -f "$temp_file"
return 1
fi
# 验证文件内容
local validation_result=$(validate_env_file "$temp_file")
if [ -n "$validation_result" ]; then
echo "验证失败: $validation_result"
log_message "WARNING" "验证失败: $env_name - $validation_result"
echo -n "是否继续加载?(y/N): "
read -r confirm
if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then
rm -f "$temp_file"
return 1
fi
fi
# 加载环境变量
echo "加载环境: $env_name"
# 备份当前重要环境变量
backup_env_variables
# 执行加载
if source "$temp_file"; then
echo "环境加载成功"
log_message "INFO" "加载成功: $env_name"
export CURRENT_ENV="$env_name"
else
echo "环境加载失败"
log_message "ERROR" "加载失败: $env_name"
# 恢复备份的环境变量
restore_env_variables
fi
# 清理临时文件
rm -f "$temp_file"
}
# 验证环境文件
validate_env_file() {
local file="$1"
local errors=""
# 检查是否有危险的命令
if grep -q -E "(rm\s+-rf|mkfs|dd\s+if=|:(){:|:&};:|chmod\s+777)" "$file"; then
errors="文件包含危险命令"
fi
# 检查是否有可疑的变量名
if grep -q -E "^(export\s+)?(PASSWORD|SECRET|KEY|TOKEN)=" "$file"; then
errors="${errors}包含敏感变量名; "
fi
# 检查是否有外部命令执行
if grep -q -E "\$(\(|`)" "$file"; then
errors="${errors}包含命令替换; "
fi
echo "$errors"
}
# 备份环境变量
backup_env_variables() {
# 创建备份文件
local backup_file="/tmp/env_backup.$$"
# 备份重要变量
{
echo "# 环境变量备份 - $(date)"
echo "# 用户: $(whoami)"
echo "# 主机: $(hostname)"
echo ""
# 备份PATH
echo "OLD_PATH=\"$PATH\""
# 备份其他重要变量
for var in PATH LD_LIBRARY_PATH PYTHONPATH JAVA_HOME HOME USER; do
if [ -n "${!var}" ]; then
echo "OLD_${var}=\"${!var}\""
fi
done
} > "$backup_file"
export ENV_BACKUP_FILE="$backup_file"
}
# 恢复环境变量
restore_env_variables() {
if [ -n "$ENV_BACKUP_FILE" ] && [ -f "$ENV_BACKUP_FILE" ]; then
echo "恢复环境变量..."
source "$ENV_BACKUP_FILE"
# 恢复PATH
if [ -n "$OLD_PATH" ]; then
export PATH="$OLD_PATH"
fi
# 清理备份文件
rm -f "$ENV_BACKUP_FILE"
unset ENV_BACKUP_FILE
echo "环境变量已恢复"
fi
}
# 创建新的环境文件
create_env() {
local env_name="$1"
local env_file="$ENV_DIR/$env_name.env"
if [ -z "$env_name" ]; then
echo "使用方法: create_env 环境名"
return 1
fi
if [ -f "$env_file" ] || [ -f "$env_file.enc" ]; then
echo "错误: 环境 '$env_name' 已存在"
return 1
fi
# 使用编辑器创建文件
echo "创建新环境文件: $env_file"
echo "# 环境: $env_name" > "$env_file"
echo "# 创建时间: $(date)" >> "$env_file"
echo "" >> "$env_file"
echo "# 示例:" >> "$env_file"
echo "# export VAR_NAME=\"value\"" >> "$env_file"
echo "# alias ll='ls -la'" >> "$env_file"
echo "" >> "$env_file"
# 打开编辑器
${EDITOR:-vi} "$env_file"
# 询问是否加密
echo -n "是否加密此环境文件?(Y/n): "
read -r encrypt_choice
if [ "$encrypt_choice" != "n" ] && [ "$encrypt_choice" != "N" ]; then
encrypt_env "$env_name"
fi
log_message "INFO" "创建环境: $env_name"
}
# 显示环境内容
show_env() {
local env_name="$1"
local enc_file="$ENV_DIR/$env_name.env.enc"
local temp_file="/tmp/$env_name.show.$$"
if [ -z "$env_name" ]; then
echo "使用方法: show_env 环境名"
return 1
fi
if [ ! -f "$enc_file" ]; then
echo "错误: 环境不存在 - $env_name"
return 1
fi
# 解密并显示
openssl enc -aes-256-cbc -d -in "$enc_file" -out "$temp_file" -pass file:"$KEY_FILE" 2>/dev/null
if [ $? -eq 0 ]; then
echo "=== 环境: $env_name ==="
cat "$temp_file"
echo "========================"
rm -f "$temp_file"
else
echo "解密失败"
rm -f "$temp_file"
return 1
fi
}
# 主函数
main() {
init_loader
echo "=== 安全环境加载器 ==="
echo "1. 创建新环境"
echo "2. 加载环境"
echo "3. 加密环境"
echo "4. 查看环境内容"
echo "5. 查看日志"
echo "6. 退出"
echo -n "请选择 [1-6]: "
read -r choice
case $choice in
1)
echo -n "请输入环境名: "
read -r env_name
create_env "$env_name"
;;
2)
echo -n "请输入要加载的环境名(留空查看列表): "
read -r env_name
load_env "$env_name"
;;
3)
echo -n "请输入要加密的环境名: "
read -r env_name
encrypt_env "$env_name"
;;
4)
echo -n "请输入要查看的环境名: "
read -r env_name
show_env "$env_name"
;;
5)
echo "=== 加载日志 ==="
tail -20 "$LOG_FILE"
;;
6)
echo "退出"
exit 0
;;
*)
echo "无效选择"
;;
esac
}
# 运行
main
# 错误:bash: source: 文件名: 没有那个文件或目录
# 原因:文件不存在或路径错误
# 解决方案:
# 1. 检查文件是否存在
ls -la 文件名
# 2. 使用绝对路径或正确相对路径
source /home/user/.bashrc # 绝对路径
source ~/.bashrc # 家目录
source ./config.sh # 当前目录
source ../config/config.sh # 上级目录
# 3. 检查文件权限
ls -la .bashrc
# 需要读取权限,如果是脚本还需要执行权限
# 4. 使用find命令查找文件
find / -name ".bashrc" 2>/dev/null
# 5. 检查文件名是否正确
# 注意大小写:.bashrc 不是 .Bashrc
# 6. 对于点命令,注意空格
. ./file.sh # 正确
. ./file.sh # 错误,点后无空格
source ./file.sh # 使用source更清晰
# 7. 如果是符号链接,检查链接目标
ls -la ~/.bashrc
readlink ~/.bashrc
# 8. 检查文件系统挂载
# 如果文件在远程或特殊文件系统,确保已挂载
# 9. 使用条件判断避免错误
if [ -f ~/.bashrc ]; then
source ~/.bashrc
else
echo "文件不存在"
fi
# 10. 使用默认文件作为备选
if [ -f ~/.custom_bashrc ]; then
source ~/.custom_bashrc
elif [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
# 问题:source后变量未正确设置或被覆盖
# 原因:作用域问题或变量名冲突
# 解决方案:
# 1. 使用export确保变量导出
# 在配置文件中:
export PATH=$PATH:/usr/local/bin
export JAVA_HOME=/usr/lib/jvm/java-11
# 2. 检查变量是否被后续脚本覆盖
# 按顺序加载配置
source config1.sh
source config2.sh # 可能覆盖config1.sh的变量
# 3. 使用唯一变量名避免冲突
# 使用前缀
export MYAPP_PATH=/opt/myapp
export MYAPP_CONFIG=/etc/myapp.conf
# 4. 使用local关键字避免影响全局(在函数中)
myfunc() {
local local_var="只在函数内有效"
export global_var="全局有效"
}
# 5. 检查变量作用域
# script.sh内容:
# var="script_value"
# 执行:
var="global_value"
source script.sh
echo $var # 输出: script_value(被修改)
# 6. 使用只读变量保护重要变量
readonly IMPORTANT_VAR="important"
source some_script.sh # 如果尝试修改IMPORTANT_VAR会报错
# 7. 备份重要变量
OLD_PATH=$PATH
source config.sh
# 如果出现问题,可以恢复
# PATH=$OLD_PATH
# 8. 检查变量引用是否正确
# 错误的引用方式
export PATH=PATH:/new/path # 缺少$
# 正确的引用方式
export PATH=$PATH:/new/path
# 9. 使用env命令检查所有环境变量
env | grep PATH
# 10. 使用declare查看变量属性
declare -p PATH # 查看PATH变量的定义
# 问题:source导致无限循环或递归调用
# 原因:配置文件互相调用或循环引用
# 解决方案:
# 1. 避免配置文件循环引用
# .bashrc 中:
if [ -f ~/.bash_aliases ]; then
source ~/.bash_aliases # 正确
fi
# 不要在.bash_aliases中再source .bashrc
# 2. 使用标志变量防止重复加载
if [ -z "$BASHRC_LOADED" ]; then
export BASHRC_LOADED=1
# 加载配置...
source ~/.bash_aliases
fi
# 3. 检查脚本是否source自身
# script.sh中:
# source script.sh # 错误!会导致递归
# 4. 使用绝对路径避免意外
source /home/user/.bashrc # 而不是 source ~/.bashrc
# 5. 限制source深度
export SOURCE_DEPTH=${SOURCE_DEPTH:-0}
if [ $SOURCE_DEPTH -gt 5 ]; then
echo "警告:source调用深度超过5层"
return 1
fi
export SOURCE_DEPTH=$((SOURCE_DEPTH + 1))
source some_file.sh
export SOURCE_DEPTH=$((SOURCE_DEPTH - 1))
# 6. 使用trap捕获问题
cleanup() {
echo "清理中..."
# 清理操作
}
trap cleanup EXIT INT TERM
# 7. 检查文件是否在source自身
if [ "$BASH_SOURCE" = "$0" ]; then
echo "脚本直接执行"
else
echo "脚本被source"
fi
# 8. 使用安全source函数
safe_source() {
local file="$1"
local max_depth=10
local current_depth=${SAFE_SOURCE_DEPTH:-0}
if [ $current_depth -ge $max_depth ]; then
echo "错误:source深度超过限制"
return 1
fi
export SAFE_SOURCE_DEPTH=$((current_depth + 1))
if [ -f "$file" ]; then
source "$file"
else
echo "文件不存在: $file"
fi
export SAFE_SOURCE_DEPTH=$current_depth
}
# 使用
safe_source ~/.bashrc
# 9. 记录source调用
export SOURCE_LOG="/tmp/source_log.$$"
echo "source $1" >> "$SOURCE_LOG"
source "$1"
# 问题:source大文件导致Shell启动慢或内存占用高
# 原因:配置文件太大或包含复杂操作
# 解决方案:
# 1. 分割大配置文件
# 将.bashrc分割成多个小文件
source ~/.bash_aliases
source ~/.bash_functions
source ~/.bash_env
# 2. 延迟加载
# 使用函数包装,需要时才加载
load_completion() {
source /etc/bash_completion
unset -f load_completion
}
# 3. 条件加载
# 只在需要时加载
if [ -n "$PS1" ]; then
# 交互式Shell才加载
source ~/.bash_interactive
fi
# 4. 使用缓存
if [ ! -f ~/.bash_cache ] || [ ~/.bashrc -nt ~/.bash_cache ]; then
# 重新生成缓存
source ~/.bashrc > ~/.bash_cache 2>&1
else
# 使用缓存
source ~/.bash_cache
fi
# 5. 优化配置文件
# 避免在配置文件中执行耗时操作
# 错误:在.bashrc中
# find / -name "*.txt" 2>/dev/null # 非常慢!
# 6. 使用后台加载
# 将耗时操作放到后台
(
# 后台执行的初始化
source ~/.slow_init.sh
) &
# 7. 按需加载函数
# 使用函数包装,第一次调用时加载
git_prompt() {
# 第一次调用时加载git-prompt
if ! type -t __git_ps1 >/dev/null; then
source /usr/share/git-core/contrib/completion/git-prompt.sh
fi
__git_ps1 "$@"
}
# 8. 使用lazy source技术
lazy_source() {
local file="$1"
local var_name="LAZY_LOADED_$(echo "$file" | tr './' '__')"
if [ -z "${!var_name}" ]; then
source "$file"
export "$var_name=1"
fi
}
# 使用
lazy_source ~/.bash_completion
# 9. 配置文件分析工具
analyze_bashrc() {
echo "分析 ~/.bashrc:"
echo "总行数: $(wc -l < ~/.bashrc)"
echo "空行数: $(grep -c '^$' ~/.bashrc)"
echo "注释行数: $(grep -c '^#' ~/.bashrc)"
echo "source命令数: $(grep -c '^source\|^\.' ~/.bashrc)"
echo "最慢的命令:"
# 这里可以添加性能分析
}
# 10. 使用profiling
# 在~/.bashrc开头添加:
# PS4='+ $(date "+%s.%N")\011 '
# exec 3>&2 2>/tmp/bashstart.$$.log
# set -x
# 在结尾添加:
# set +x
# exec 2>&3 3>&-
# 然后分析日志文件
source file.sh - 执行脚本. file.sh - 点命令(相同功能)source ~/.bashrc - 重载配置source /etc/profile - 加载系统配置~/.bashrc - Bash配置文件~/.bash_profile - Bash登录配置~/.profile - 用户环境配置/etc/environment - 系统环境变量source script.sh # 在当前Shell执行,变量会保留
bash script.sh # 在子Shell执行,变量不保留
./script.sh # 在子Shell执行,需要执行权限
source加载配置,使用bash执行独立脚本export确保变量被导出.)保持POSIX兼容性