
ファイルのリストを再帰的に検索し、ファイルに文字列が含まれている場合は、その文字列の 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