ファイル内の文字列を検索し、結果に応じて複数のファイルの名前を変更します。

ファイル内の文字列を検索し、結果に応じて複数のファイルの名前を変更します。

ファイルのリストを再帰的に検索し、ファイルに文字列が含まれている場合は、その文字列の grep 結果にファイルの名前を変更しようとしています。

サンプル ファイルには次の内容が含まれています。

file1   
foo bar1

file2
foo bar2

file3
foo bar3

file4
foo bar4

file5
foo bar5

grep + awk は必要な結果を返します:

$ grep -r "^foo" . | awk '{print $2}'
bar1
bar2
bar3
bar4
bar5

これらの結果を mv コマンドに渡すことができません。

$ grep -r "^foo" . | awk '{print $2}' | xargs -I{} mv {} .
mv: cannot stat 'bar1': No such file or directory
mv: cannot stat 'bar2': No such file or directory
mv: cannot stat 'bar3': No such file or directory
mv: cannot stat 'bar4': No such file or directory
mv: cannot stat 'bar5': No such file or directory

よろしくお願いします。Gnu/BSD Grep はどちらも同じ結果になります。

答え1

シェルの for ループを使用します:

for match in "$(grep -ro '^foo.*')";do
    echo mv "${match%:*}" "${match#*:}"
done

これにより、一致するものがすべて反復処理されfile:matching-substring、文字列演算子%#文字列演算子を使用して、 までのすべて、 以降のすべて、および を含むすべてが削除されます:

パターンに一致する部分文字列だけではなく、行全体を一致させたい場合には、

for match in $(grep -r '^foo');do

一致やファイル名にスペースが含まれる可能性があるので、必ず二重引用符で囲んでください。

1 つのパターンで一致させながら、一致する行の 2 番目の単語にファイル名を変更する場合:

for match in "$(grep -ro '^foo.*')";do
    fname=$("echo ${match#*:}|awk '{print $2}'")
    echo mv "${match%:*}" "$fname"
done

答え2

Perl を使用すると、目的を達成できます。

#!/usr/bin/env perl
use strict;

my $dir = "/path/to/directory";
chdir $dir or die "Cannot open $dir!";

my @files = grep { -f } glob("*");

foreach my $file(@files) {
  open F1, "<", $file or die "Cannot open file $file! $!\n";
  while ( <F1> ){
    chomp;
    my @strings = split(' ');
    if($strings[1] =~ /bar/){
      system("/bin/mv $file $strings[1]");
    }
  }
  close(F1);
}

答え3

find . -type f -exec egrep -il '^foo' {} \; | sort | while IFS='' read -r line; do mv -n "$line" "$(dirname "$line")"'/'"$(egrep -i '^foo' "$line" | awk '{ print $2 }')"; done

関連情報