bash:用于识别导致错误的特定别名的脚本

时间:2019-04-17 15:26:00

标签: bash shell alias arch

[带有GNU bash 5.0.3的Arch Linux v5.0.7]

某些.bashrc别名似乎与bashpyenv提供的pyenv-virtualenvwrapper shell脚本冲突。
我跟踪了运行脚本的问题,使用set -x并启用了所有别名,最后看到只有使用unalias -a禁用别名时,脚本才能正常退出,退出代码为0。因此,这与别名有关……但是哪一个呢?

要尝试自动化,我在下面编写了shell脚本:

  • 它一次取消别名的别名,从别名的完整列表中迭代读取,
  • 它针对那个遗忘的别名配置测试有冲突的shell脚本test.sh,并在检测到错误的情况下打印出一些内容,
  • 它取消了以前的非锯齿处理,
  • 继续取消混叠下一个别名。

但是两个内置aliasunalias在下面的脚本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都可以。它只需要向stderr返回一些非空字符串即可。

第一个异常情况是文件aliases.txt为空,好像无法从脚本内访问alias内置文件一样。如果我使用已经填充的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

2 个答案:

答案 0 :(得分:1)

我通常建议完全不要将其实现为外部脚本,因为它可以直接在您的交互式shell中进行评估(毕竟,所有可能涉及的别名都在其中)是有意义的定义)。

print_result() {
  local prior_retval=$? label=$1
  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 "$1"); print_result "Unchanged aliases"
  for alias in "${!BASH_ALIASES[@]}"; do
    (unalias "$alias" && eval "$1"); print_result "Without $alias"
  done
}

请考虑以下内容:

rm_in_home_only() { [[ $1 = /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编码内对函数进行了定义定义;由于别名是解析器的行为,因此别名发生在定义的函数中,而不是在函数运行的情况下发生。

答案 1 :(得分:0)

@ Kamil_Cuk,@ Benjamin_W和@cdarke都指出了这样一个事实,即非交互式外壳(如从bash脚本生成的外壳)无法访问别名。

@CharlesDuffy指出可能的单词拆分和glob扩展导致某些内容在上面的原始[ -z $(eval "$*" 1> /dev/null) ]块中可能是无效的测试语法,或者更糟的是将$(eval "$*" 1> /dev/null)解析为glob的可能性导致脚本行为无法预测。屏蔽已更正为:[ -z "$(eval "$*" 1> /dev/null)" ]

使cac.sh产生的外壳与#! /usr/bin/bash -i进行交互。使两个内置aliasunalias在调用时返回非空结果,并且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