bash:用于识别导致错误的特定别名的脚本
bash: script to identify specific alias causing a bug
[Arch Linux v5.0.7 与 GNU bash 5.0.3]
一些 .bashrc
别名似乎与 bash
shell 脚本冲突 pyenv
和 pyenv-virtualenvwrapper
。
我跟踪了解决问题 运行 脚本,使用 set -x
并启用所有别名,最后看到只有当使用 unalias -a
禁用别名时,脚本才能正常退出,退出代码为 0。所以这与别名有关......但是哪一个?
为了尝试自动化,我在下面编写了 shell-脚本:
- 它一次取消一个别名,从完整的别名列表中迭代读取,
- 它针对该留一法别名配置测试冲突的 shell 脚本
test.sh
,并打印一些内容以防检测到错误,
- 取消之前的去锯齿,
- 它继续取消下一个别名的别名。
但是两个内置函数 alias
和 unalias
在下面的脚本 cac.sh
中表现不佳:
#! /usr/bin/bash
[ -e aliases.txt ] && rm -f aliases.txt
alias | sed 's/alias //' | cut -d "=" -f1 > aliases.txt
printf "File aliases.txt created with %d lines.\n" \
"$(wc -l < <(\cat aliases.txt))"
IFS=" "
n=0
while read -r line || [ -n "$line" ]; do
n=$((n+1))
aliasedAs=$( alias "$line" | sed 's/alias //' )
printf "Line %2d: %s\n" "$n" "$aliasedAs"
unalias "$line"
[ -z $(eval "$*" 1> /dev/null) ] \ # check output to stderr only
&& printf "********** Look up: %s\n" "$line"
eval "${aliasedAs}"
done < <(tail aliases.txt) # use tail + proc substitution for testing only
像这样使用脚本:$ cac.sh test.sh [optional arguments to test.sh]
任何 test.sh
都可以。它只需要 return 一些非空字符串到 stderr。
第一个异常是文件 aliases.txt
是空的,就好像无法从脚本中访问 alias
内置函数一样。如果我从第 3 行开始脚本,使用已经填充的 aliases.txt
文件,脚本会在 while 块内的第二行失败,就像无法从脚本中调用 alias
一样。 任何建议表示赞赏。
注意:下面的一行在控制台中有效:
$ n=0;while read -r line || [ -n "$line" ]; do n=$((n+1)); printf "alias %d : %s\n" "$n" "$(alias "$line" | sed 's/alias //')"; done < aliases.txt
@Kamil_Cuk、@Benjamin_W 和@cdarke 都指出了这样一个事实,即非交互式 shell(从 bash
脚本生成)没有访问别名。
@CharlesDuffy 指出可能的单词拆分和 glob 扩展导致上面原始 [ -z $(eval "$*" 1> /dev/null) ]
块中的某些测试语法无效,或者更糟糕的是 $(eval "$*" 1> /dev/null)
可能被解析为导致不可预测的脚本行为的 glob。块更正为:[ -z "$(eval "$*" 1> /dev/null)" ]
.
使 cac.sh
生成的 shell 与 #! /usr/bin/bash -i
交互。使两个内置函数 alias
和 unalias
在调用时返回非空结果,并且 BASH_ALIASES[@]
可以从脚本中访问。
#! /usr/bin/bash -i
[ -e aliases.txt ] && rm -f aliases.txt
alias | sed 's/alias //' | cut -d "=" -f1 > aliases.txt
printf "File aliases.txt created with %d lines.\n" \
"$(wc -l < <(\cat aliases.txt))"
IFS=" "
while read -r line || [ -n "$line" ]; do
aliasedAs=$( alias "$line" | sed 's/alias //' )
unalias "$line"
[ -z "$(eval "$*" 2>&1 1>/dev/null)" ] \ # check output to stderr only
&& printf "********** Look up: %s\n" "$line"
eval "${aliasedAs}"
done < aliases.txt
警告: 测试 test.sh
求助于 eval
内置。如果 test.sh
和可选参数不是来自受信任的来源,Arbitrary code 可以在您的系统上执行。
我通常建议完全不要将其实现为外部脚本——作为一个可以在交互式 shell 中直接求值的函数更有意义(毕竟,所有定义了可能涉及的别名。
print_result() {
local prior_retval=$? label=
if (( prior_retval == 0 )); then
printf '%-30s - %s\n' "$label" WORKS >&2
else
printf '%-30s - %s\n' "$label" BROKEN >&2
fi
}
test_without_each_alias() {
[ "$#" = 1 ] || { echo "Usage: test_without_each_alias 'code here'" >&2; return 1; }
local alias
(eval ""); print_result "Unchanged aliases"
for alias in "${!BASH_ALIASES[@]}"; do
(unalias "$alias" && eval ""); print_result "Without $alias"
done
}
考虑以下几点:
rm_in_home_only() { [[ = /home/* ]] || return 1; rm -- "$@"; }
alias rm=rm_in_home_only # alias actually causing our bug
alias red_herring=true # another alias that's harmless
test_without_each_alias 'touch /tmp/foobar; rm /tmp/foobar; [[ ! -e /tmp/foobar ]]'
...发出类似的东西:
Unchanged aliases - BROKEN
Without rm - WORKS
Without red_herring - BROKEN
请注意,如果您传递的代码执行一个函数,您需要确保该函数在 eval
代码中 已定义;由于别名是解析器行为,它们发生在函数被定义为 时,而不是函数被定义为 运行.
时
[Arch Linux v5.0.7 与 GNU bash 5.0.3]
一些 .bashrc
别名似乎与 bash
shell 脚本冲突 pyenv
和 pyenv-virtualenvwrapper
。
我跟踪了解决问题 运行 脚本,使用 set -x
并启用所有别名,最后看到只有当使用 unalias -a
禁用别名时,脚本才能正常退出,退出代码为 0。所以这与别名有关......但是哪一个?
为了尝试自动化,我在下面编写了 shell-脚本:
- 它一次取消一个别名,从完整的别名列表中迭代读取,
- 它针对该留一法别名配置测试冲突的 shell 脚本
test.sh
,并打印一些内容以防检测到错误, - 取消之前的去锯齿,
- 它继续取消下一个别名的别名。
但是两个内置函数 alias
和 unalias
在下面的脚本 cac.sh
中表现不佳:
#! /usr/bin/bash
[ -e aliases.txt ] && rm -f aliases.txt
alias | sed 's/alias //' | cut -d "=" -f1 > aliases.txt
printf "File aliases.txt created with %d lines.\n" \
"$(wc -l < <(\cat aliases.txt))"
IFS=" "
n=0
while read -r line || [ -n "$line" ]; do
n=$((n+1))
aliasedAs=$( alias "$line" | sed 's/alias //' )
printf "Line %2d: %s\n" "$n" "$aliasedAs"
unalias "$line"
[ -z $(eval "$*" 1> /dev/null) ] \ # check output to stderr only
&& printf "********** Look up: %s\n" "$line"
eval "${aliasedAs}"
done < <(tail aliases.txt) # use tail + proc substitution for testing only
像这样使用脚本:$ cac.sh test.sh [optional arguments to test.sh]
任何 test.sh
都可以。它只需要 return 一些非空字符串到 stderr。
第一个异常是文件 aliases.txt
是空的,就好像无法从脚本中访问 alias
内置函数一样。如果我从第 3 行开始脚本,使用已经填充的 aliases.txt
文件,脚本会在 while 块内的第二行失败,就像无法从脚本中调用 alias
一样。 任何建议表示赞赏。
注意:下面的一行在控制台中有效:
$ n=0;while read -r line || [ -n "$line" ]; do n=$((n+1)); printf "alias %d : %s\n" "$n" "$(alias "$line" | sed 's/alias //')"; done < aliases.txt
@Kamil_Cuk、@Benjamin_W 和@cdarke 都指出了这样一个事实,即非交互式 shell(从 bash
脚本生成)没有访问别名。
@CharlesDuffy 指出可能的单词拆分和 glob 扩展导致上面原始 [ -z $(eval "$*" 1> /dev/null) ]
块中的某些测试语法无效,或者更糟糕的是 $(eval "$*" 1> /dev/null)
可能被解析为导致不可预测的脚本行为的 glob。块更正为:[ -z "$(eval "$*" 1> /dev/null)" ]
.
使 cac.sh
生成的 shell 与 #! /usr/bin/bash -i
交互。使两个内置函数 alias
和 unalias
在调用时返回非空结果,并且 BASH_ALIASES[@]
可以从脚本中访问。
#! /usr/bin/bash -i
[ -e aliases.txt ] && rm -f aliases.txt
alias | sed 's/alias //' | cut -d "=" -f1 > aliases.txt
printf "File aliases.txt created with %d lines.\n" \
"$(wc -l < <(\cat aliases.txt))"
IFS=" "
while read -r line || [ -n "$line" ]; do
aliasedAs=$( alias "$line" | sed 's/alias //' )
unalias "$line"
[ -z "$(eval "$*" 2>&1 1>/dev/null)" ] \ # check output to stderr only
&& printf "********** Look up: %s\n" "$line"
eval "${aliasedAs}"
done < aliases.txt
警告: 测试 test.sh
求助于 eval
内置。如果 test.sh
和可选参数不是来自受信任的来源,Arbitrary code 可以在您的系统上执行。
我通常建议完全不要将其实现为外部脚本——作为一个可以在交互式 shell 中直接求值的函数更有意义(毕竟,所有定义了可能涉及的别名。
print_result() {
local prior_retval=$? label=
if (( prior_retval == 0 )); then
printf '%-30s - %s\n' "$label" WORKS >&2
else
printf '%-30s - %s\n' "$label" BROKEN >&2
fi
}
test_without_each_alias() {
[ "$#" = 1 ] || { echo "Usage: test_without_each_alias 'code here'" >&2; return 1; }
local alias
(eval ""); print_result "Unchanged aliases"
for alias in "${!BASH_ALIASES[@]}"; do
(unalias "$alias" && eval ""); print_result "Without $alias"
done
}
考虑以下几点:
rm_in_home_only() { [[ = /home/* ]] || return 1; rm -- "$@"; }
alias rm=rm_in_home_only # alias actually causing our bug
alias red_herring=true # another alias that's harmless
test_without_each_alias 'touch /tmp/foobar; rm /tmp/foobar; [[ ! -e /tmp/foobar ]]'
...发出类似的东西:
Unchanged aliases - BROKEN
Without rm - WORKS
Without red_herring - BROKEN
请注意,如果您传递的代码执行一个函数,您需要确保该函数在 eval
代码中 已定义;由于别名是解析器行为,它们发生在函数被定义为 时,而不是函数被定义为 运行.