Bash - 関数で使用されている関数を別のスクリプトに再帰的にコピーする方法

Bash - 関数で使用されている関数を別のスクリプトに再帰的にコピーする方法

.bashrc私のファイルはどのように共有されますが、関連部分のみを共有しますか?

たとえば、次の場所に5つの関数を作成しました.bashrc

f1() {
...
}

f2() {
   f1
   ...
}

f3() {
   f2
   ...
}

f4() {
   f1
   f3
   ...
}

f5() {
   ...
}

f5()ケース1:同僚と共有したいので、コピーしてf5()貼り付けるだけです。

ケース2:共有したいので、コピーして再帰的に合計しf3()たいです。f3()f2()f1()

ケース3:共有したいので、ANDを再帰的にf4()コピーしたいのですが、どちらもANDとして呼び出されても一度だけコピーしたいと思います。f3()f2()f1()f1()f1()f3()f2()

今は手動で見つけてコピーしますが、エラーが発生しやすいです。これを自動的に実行できますか?私はPythonを使用しません。すべての関数が同じ.bashrcファイルにあり、私が定義したbash関数であるとします。

答え1

見るflatten.sh。私はすべてのエイリアスと関数をシェルスクリプトにソース化し、必要に応じてフラット化できるようにこの記事を書きました。だから今、あなたがたどった状況に似ています。

したがって、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)を作成してから印刷します。

このスクリプトは概念的に非常に単純で無差別です。パフォーマンスやシンプルさのためにコードを最適化する努力はまったく行われていません。

注:これは完全なシェルコードパーサーではありません(せいぜい単純な正規表現トークンマッチングです)。上記のサンプル関数を定義するシェルコード、私自身の〜/ .bashrc、および組み込み関数出力のstdinに対してのみsetテストされました(定義された関数と変数を内部help setまたは外部でも印刷します)。 bashのマニュアルページ)。

他のシェルコードがそれを破る可能性があります(事実の可能性が高い)。この場合(少なくとも)トークン抽出正規表現を変更する必要があり、照会関数定義の先頭の正規表現も変更する必要があります。引用符付き文字列とコメントを削除するコードがあるかもしれません。これは、失敗の可能性が最も高い3つのポイントです。

#!/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
   ...
}

f2f1への呼び出しが含まれているため、f1とf2が印刷されます。

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

f2() {
   f1
   ...
}

f3() {
   f2
   ...
}

f4() {
   f1
   f3
   ...
}

f4f1 と f3 への呼び出しが含まれ、f3 には f2 への呼び出しが含まれ、f2 には f1 への呼び出しが含まれているため、f1、f2、f3、および f4 が印刷されます。 f1 は f4 と f2 から呼び出されても一度だけ印刷します。

貼り付けまたはxsel -i -b編集メニューで使用するために出力をクリップボードにパイプすることができます。またはマウスの中央ボタンをクリックして貼り付けるには、Xのデフォルト選択にコピーします。望むより。CtrlVShiftInsxsel -iman xsel

関連情報