Bash 的坑

nameref 的命名冲突

nameref 不只是传参和接收参数的变量不能同名。 在同一个函数作用域里的其他变量也不能同名,参考下面的代码。

https://gist.github.com/4b1b09325b64c72844ea9112cad650fc#file-1-bash

VAR=VALUE command 的异常行为

我原先一直以为 VAR=VALUE 是只针对当前行生效的,不会在全局生效。实际上会根据 command 不同而产生不同的效果。

比如这个例子

原因参考这两个

  • https://unix.stackexchange.com/a/264642
  • https://unix.stackexchange.com/a/458901

声明 Associative Array 必须要写 declare -A

错误声明

foo=abc
M1=([foo]=1)
echo "${M1[foo]}" # print 1
echo "${M1[abc]}" # print 1

fo=ab
M2=(['fo']=2)
echo "${M2[fo]}" # print 2
echo "${M2[ab]}" # print 2

f=a
M3[f]=3
echo "${M3[f]}" # print 3
echo "${M3[a]}" # print 3

正确声明

foo=abc
declare -A M1=([foo]=1)
echo "${M1[foo]}" # print 1
echo "${M1[abc]}" # print empty

fo=ab
declare -A M2=(['fo']=2)
echo "${M2[fo]}" # print 2
echo "${M2[ab]}" # print empty

f=a
declare -A M3[f]=3
echo "${M3[f]}" # print 3
echo "${M3[a]}" # print empty

shift 的重要性

文件 a.sh

echo "$@"

文件 b.sh

#!/usr/bin/env bash

source ./a.sh

function load() {
  local path=$1
  source "$path"
}

load ./a.sh

执行

chmod +x ./b.sh
./b.sh hello
# 输出
# hello
# ./a.sh

:当前脚本以及函数的参数 $@source 传递给下一个脚本的 $@

bash 5 实测,source 改成 . 也是一样的结果。

@TODO: bash 4 待测

详见 https://stackoverflow.com/a/65912397

数组没法 export

bash 5 实测,这 bug 还是存在。

export arr=( 1 2 3)
echo "${arr[@]}"
bash -c 'echo "${arr[@]}"'  # It prints empty
  • https://stackoverflow.com/questions/5564418/exporting-an-array-in-bash-script
  • https://www.mail-archive.com/[email protected]/msg01774.html

数组赋值在管道执行完毕后会重置

printf 'a.c\nb.z\nc.z\n' | while read -r path; do
  COMPREPLY+=( "$path" )
done
echo "${COMPREPLY[@]}"

打印出来是空的。因为默认情况下所有管道内的命令都是运行在子进程里的。 即 while readCOMPREPLY+=( "$path" ) 都运行在子进程,所以父进程的 COMPREPLY 值并没有改变。

解决方法

改写成这样

while read -r path; do
  COMPREPLY+=( "$path" )
done < <(printf 'a.c\nb.z\nc.z\n')
echo "${COMPREPLY[@]}"

或者使用 shopt -s lastpipe 开启 lastpipe,可以让管道中最后一个命令运行在当前 shell 环境。(这个方法可能没用)

相关链接 https://stackoverflow.com/questions/36340599/how-does-shopt-s-lastpipe-affect-bash-script-behavior