Bash - 如何将函数中使用的函数递归复制到另一个脚本(与其他人共享我的 .bashrc 文件,但仅共享相关部分)

Bash - 如何将函数中使用的函数递归复制到另一个脚本(与其他人共享我的 .bashrc 文件,但仅共享相关部分)

如何共享我的.bashrc文件但仅共享相关部分?

例如,我在以下位置创建了 5 个函数.bashrc

f1() {
...
}

f2() {
   f1
   ...
}

f3() {
   f2
   ...
}

f4() {
   f1
   f3
   ...
}

f5() {
   ...
}

案例一:我想分享f5()给同事,所以我只需复制f5()粘贴给他们

情况2:我想分享f3(),所以我想复制f3()并递归f2()f1()

情况3:我想分享f4(),所以我想复制f3()并递归地f2()f1();但我只想复制f1()一次,即使在和f1()中都被调用。f3()f2()

现在我手动查找并复制,但很容易出错;可以自动完成吗?我不使用Python。假设所有函数都在同一个.bashrc文件中,都是我定义的bash函数。

答案1

看一下展平.sh。我写这个是为了我可以将所有别名和函数源到一个 shell 脚本中,并将其扁平化为所需的内容,所以就像您现在所处的情况一样。

因此,像example.lib这样(或你的.bashrc):

f1() {
  echo "I am f1"
}

f2() {
   f1
}

f3() {
   f2
}

f4() {
   f1
   f3
}

f5() {
  :
}

example.sh和这样的脚本:

#!/usr/bin/env -S bash - 
. /path/to/example.lib

f4

你跑flatten.sh example.sh并得到

#!/usr/bin/env -S bash - 
f1() {
  echo "I am f1"
}

f2() {
   f1
}

f3() {
   f2
}

f4() {
   f1
   f3
}

f4

答案2

以下 perl 脚本是一个概念验证演示,用于从脚本中提取函数名称和定义。它构建一个%funcs包含每个函数代码的哈希(关联数组)。然后,它搜索每个函数定义,找出该函数中调用了哪些其他函数,并将该信息存储在名为 的散列中的散列(“HoH”,一个散列,其中每个元素都是另一个散列。请参阅man perldsc)中%contains

最后,它从命令行上提供的函数名称列表开始,构建要打印的函数列表(另一个称为 的哈希值%out),然后打印它们。

该脚本在概念上相当简单且暴力 - 没有努力优化代码的性能或简洁性。

注意:这不是一个完整的 shell 代码解析器(远非如此,它充其量只是一个简单的正则表达式标记匹配器)。它仅针对定义上面示例函数的 shell 代码、我自己的 ~/.bashrc 以及来自set内置函数输出的 stdin 进行了测试(它打印定义的函数以及变量,即使在help set或在 bash 手册页中)。

其他 shell 代码有可能(事实上,很有可能)会破坏这一点。在这种情况下,您(至少)需要细化标记提取正则表达式,并且可能还需要细化查找函数定义开头的正则表达式。可能还有删除带引号的字符串和注释的代码。它们是最有可能失败的三个点。

#!/usr/bin/perl

use strict;
use v5.10;

# primitive arg handling to separate function names from
# input files on the command line.
#
# if an argument is a filename that exists, treat it as
# an input file.  If not, treat it as a function name to
# search for.

my (@args,%find) = ();
foreach (@ARGV) {
  if (-e $_) {
    push @args, $_; # array of input files to process
  } else {
    $find{$_} = 1;  # hash of function name(s) to search for.
  }
};
@ARGV = @args;

# Main loop, read and process the input.
# Build up a hash called %funcs with key = function name
# and val = function code.
my %funcs = ();
while(<>) {
  state ($fname, $in_func, $counter);
  # state variables:
  # $fname   - name of current function
  # $in_func - are we in a function definition
  # $counter - how many { and } have we seen in this function?

  if (/^(?:function)?\s*([^\$(=\s]*)\s*[(]/) {
    $fname   = $1;
    $funcs{$fname} = $_;
    $in_func = 1;

    # cuddled braces begin on same line as function name. uncuddled don't.
    my $cuddled  = () = $_ =~ (/{/g); # count of { on this line
    next unless $cuddled;
    $cuddled    -= () = $_ =~ (/}/g); # subtract count of }s on line
    $counter = $cuddled;

    $in_func = $cuddled;
    next;
  };

  if ($in_func) {
    $funcs{$fname} .= $_;

    my $plus  = () = $_ =~ (/{/g); # count of {s on line
    my $minus = () = $_ =~ (/}/g); # count of }s on line

    $counter += $plus - $minus;

    $in_func = ($counter > 0);
  }
};

###
### Now determine which functions to print, then print them.
###

my %contains = ();
my $match = join("|", keys %funcs);

foreach my $f (keys %funcs) {
  # ignore everything in quoted strings and comments by
  # copying the current function to variable $function and
  # stripping unwanted junk. ignore unquoted array references  too.
  my $function;
  ($function = $funcs{$f}) =~ s/"[^"]*"|'[^']*'|#.*$|\$\{[^}]}//mg;

  # find tokens that *look like* calling one of our known function names
  my (@tokens) = ($function =~ /(?:^|;|&{1,2}|\|{1,2}|[({])\s*($match)\b(?!=)/gm);

  foreach my $t (@tokens) {
    # if the current token $t is one of our known functions
    # then add it to %contains{$f}
    if (defined($funcs{$t})) {
      $contains{$f}->{$t} = 1;
    };
  };
};

my %out = %find;

# Iterate over each function's name.  Add the name to %out 
# and %find if it is called from within a wanted function.
# Repeat until %out doesn't change.
my %old_out;
do {
  %old_out = %out;
  foreach my $f (keys %find) {
    foreach my $t (keys %{ $contains{$f} }) {
      $out{$t}  = 1; # add to output hash
      $find{$t} = 1; # add to list of function names to search for
    };
  };
} until %out == %old_out;

# print the functions listed in %out, sorted by name
# otherwise will be printed in pseudo-random order as hashes
# are un-ordered.
foreach my $f (sort keys %out) {
  print $funcs{$f}, "\n";
};

另存为,例如extract-funcs.pl,使可执行文件chmod +x像这样运行(带有函数定义的副本functions.txt):

$ ./extract-funcs.pl functions.txt f1
f1() {
...
}

f1不包含对其他函数的任何调用,因此仅打印 f1。

$ ./extract-funcs.pl functions.txt f2
f1() {
...
}

f2() {
   f1
   ...
}

f2包含对 f1 的调用,因此打印 f1 和 f2。

$ ./extract-funcs.pl functions.txt f4
f1() {
...
}

f2() {
   f1
   ...
}

f3() {
   f2
   ...
}

f4() {
   f1
   f3
   ...
}

f4包含对 f1 和 f3 的调用,f3 包含对 f2 的调用,f2 包含对 f1 的调用,因此打印 f1、f2、f3 和 f4。 f1 仅打印一次,即使它是从 f4 和 f2 调用的

您可以通过管道将输出xsel -i -b复制到剪贴板,以便与“编辑”菜单中的“粘贴”或Ctrl-VShift-一起使用Ins。或者只是xsel -i复制到 X 的主要选择以进行中键粘贴。看man xsel

相关内容