在脚本(.bashrc 等)中查找重复的别名和函数

在脚本(.bashrc 等)中查找重复的别名和函数

网站说函数比别名更快,但他正确地指出别名更容易理解 - 当您想要非常简单的东西并且不需要考虑传递参数时,别名是方便且明智的。既然如此,我的个人资料大约有 1,000 行,并且是我经常使用的功能和工具的来源,作为保留我可以参考并重用于其他任务的技术的一种手段,其中包含别名和函数。

但一个问题是别名优先于函数,并且别名和函数的重新定义可能会导致问题(例如,如果我调用了一个函数gg,然后在脚本中,意外地,我有一个名为gg- 但也如果稍后重新定义函数,再次作为函数,它会覆盖以前的定义)。配置文件已加载,但我最终遇到了问题。一种解决方案可能是消除所有别名并仅使用函数(有人这样做吗,我很想知道,因为如果我想alias m=man这样做比这样做更直观、更明智function m() { man $@; }?),但我仍然遇到函数重新定义的问题在这种情况下。

有没有一种方法可以解析脚本,其目标是回答:“对于别名或函数的每个声明,显示包含该项目的重新声明(别名或函数)的所有行”?

答案1

尝试这样的事情:

$ cat find-dupes.pl
#!/usr/bin/perl
                                                         
use strict;                                                                                                        
#use Data::Dump qw(dd);                         

# Explanation of the regexes ($f_re and $a_re):
#                                                         
# Both $f_re and $a_re start with '(?:^|&&|\|\||;|&)' to anchor
# the remainder of the expression to the start of the line or
# immediately after a ;, &, &&, or ||. Because it begins with
# '?:', this is a non-capturing sub-expression, i.e. it just    
# matches its pattern but doesn't return what it matches. 
                                                         
# $f_re has two main sub-expressions. One to match 'function name ()'
# (with 'function ' being optional) and the other to match
# 'function name () {' (with the '()' being optional).
#
# Each sub-expression contains more sub-expressions, with one of
# them being a capture group '([-\w.]+)' and the rest being       
# non-capturing (they start with '?:'). i.e. it returns the
# function name as either $1 or $2, depending on which subexp                                               
# matched.
my $f_re = qr/(?:^|&&|\|\||;|&)\s*(?:(?:function\s+)?([-\w.]+)\s*\(\)|function\s+([-\w.]+)\s+(?:\(\))?\s*\{)/;

# $a_re matches alias definitions and returns the name of
# the alias as $1.
my $a_re = qr/(?:^|&&|\|\||;|&)(?:\s*alias\s+)([-\w.]+)=/;

# %fa is a Hash-of-Hashes (HoH) to hold function/alias names and
# the files/lines they were found on. i.e an associative array
# where each element is another associative array.  Search for
# HoH in the perldsc man page.
my %fa;

# main loop, read and process the input
while(<>) {
  s/#.*|^\s*:.*//;  # delete comments
  s/'[^']+'/''/g;   # delete everything inside ' single-quotes
  s/"[^"]+"/""/g;   # delete everything inside " double-quotes
  next if /^\s*$/;  # skip blank lines

  while(/$f_re/g) {
      my $match = $1 // $2;
      #print "found: '$match':'$&':$ARGV:$.\n";
      $fa{$match}{"function $ARGV:$."}++;
  };

  while(/$a_re/g) {
      #print "found: '$1':'$&':$ARGV:$.\n";
      $fa{$1}{"alias $ARGV:$."}++;
  };

  close(ARGV) if eof;
};

#dd \%fa;

# Iterate over the function/alias names found and print the
# details of duplicates if any were found.
foreach my $key (sort keys %fa) {
  my $p = 0;

  # Is this function/alias ($key) defined more than once on
  # different lines or in different files?
  if (keys %{ $fa{$key} } > 1) {
    $p = 1;
  } else {
    # Iterate over the keys of the second-level hash to find out
    # if there is more than one definition of a function/alias
    # ($key) in the same file on the same line ($k)
    foreach my $k (keys %{ $fa{$key} }) {
      if ($fa{$key}{$k} > 1) {
        $p = 1;

        # break out of the foreach loop, there's no need to keep
        # searching once we've found a dupe
        last;
      };
    };
  };

  # print the details if there was more than one.
  print join("\n\t", "$key:", (keys %{$fa{$key}}) ), "\n\n" if $p;
};

被注释掉的数据::转储print、 和dd行用于调试。取消注释可以更好地了解该脚本的作用及其工作原理。dd该模块的函数输出Data::Dump特别有趣,因为它向您显示了 HoH 的结构(和内容)%faData::Dump不包含在 perl 中,它是您需要安装的库模块。您没有提到您正在使用什么发行版,但如果您使用的是 debian/ubuntu/mint/etc,您可以使用sudo apt install libdata-dump-perl.其他发行版可能以稍微不同的名称打包它。否则,您可以使用 来安装它cpan

示例输出(使用包含评论中的别名以及一些虚拟函数的文件):

$ cat yorsub.aliases 
function foo () { echo ; }
bar () { echo ; }
bar () { echo ; }
function baz () { echo ; } && quux () { echo ; } ; alias xyz=abc; 
type tmux  &> /dev/null && alias t='tmux'
alias cd-='cd -'; alias cd..='cd ..'; alias u1='cd ..'; alias u2='cd ../..'; alias u3='cd ../../..'; alias u4='cd ../../../../..'; alias u5='cd ../../../../../..'; alias u6='cd ../../../../../../..' alias back='cd -'; alias cd-='cd -'; alias .1="cd .."; alias .2="cd ../.."; alias .3="cd ../../.."; alias .4="cd ../../../.."; alias .5="cd ../../../../.."; alias .6='cd ../../../../../../..'
function cd.. { cd .. ; }
function abc () { xyx "$@" }; abc () { xyz } ; function abc { xyz }; alias abc=xyz
$ ./find-dupes.pl yorsub.aliases    
abc:
        function yorsub.aliases:8
        alias yorsub.aliases:8

bar:
        function yorsub.aliases:3
        function yorsub.aliases:2

cd-:
        alias yorsub.aliases:6

cd..:
        alias yorsub.aliases:6
        function yorsub.aliases:7

答案2

简单的 grep 查找定义,但不检查重新定义:

$ grep -onE 'alias [[:alnum:]_]+=|[[:alnum:]_]+\(\)' .bashrc .aliases
.bashrc:47:alias foo=
.bashrc:47:alias bar=
.bashrc:49:asfdasdf()
.aliases:3:alias ls=
.aliases:6:alias foo=

这个 Perl 一行代码保留了一个计数,以便它可以标记重新定义:

$ perl -lne 'while( /alias (\w+)=|(\w+)\(\)/g ) { 
                 $name = $1 // $2; $count{$name} += 1; 
                 printf "\"%s\" %s in %s line %s%s\n", $name, $count{$name} > 1 ? "REDEFINED" : "defined", $ARGV, $. 
             }' .bashrc .aliases 
"foo" defined in .bashrc line 47
"bar" defined in .bashrc line 47
"asfdasdf" defined in .bashrc line 49
"ls" defined in .aliases line 53
"foo" REDEFINED in .aliases line 56

(输入文件的顺序会影响哪个文件未标记为“重新定义”。)

相关内容