find -exec から {} 戻り文字列を操作する

find -exec から {} 戻り文字列を操作する

ファイルの数が多い場合に備えて、できるだけ効率的に実行したいと考えています。私が望んでいるのは、見つかったすべてのファイルの名前を変更し、サフィックスを削除することです。

例えば:

[/tmp] $ ls -l
a.log
b.log
c.tmp
[/tmp] $ find /tmp -name "*.log" -type f -exec mv {} {%.*} \;
[/tmp] $ ls -l
a
b
c.tmp

これは機能しません。通常の bash 変数であれば、最後まで${var%.*}返されます。var.

答え1

シェル パラメータ拡張演算子を使用するには、シェルを起動します。

find ~/tmp -name '*.log' -type f -exec sh -c '
  for file do
    mv -i -- "$file" "${file%.*}"
  done' sh {} +

/tmpただし、悪意のあるユーザーがファイル システム¹ 上の任意のファイルの名前を変更したり、ファイルを任意のディレクトリ² に移動したりできるようになるため、他のユーザーが書き込み可能なディレクトリではこれを行うことは避けてください.log

一部の および 実装ではfindmvおよび を使用してfind -execdir安全mv -T性を高めることができます。

find /tmp -name '*.log' -type f -execdir sh -c '
  for file do
    mv -Ti -- "$file" "${file%.*}"
  done' sh {} +

または、システム コールrenameだけを実行する (perl バリアント)を使用しrename()、ファイルを他のファイル システムまたはディレクトリに移動しないようにします...

find /tmp -name '*.log' -type f -execdir rename 's/\.log$//' {} +

または、全体を次のように実行しますperl

perl -MFile::Find -le '
  find(
    sub {
      if (/\.log\z/) {
        $old = $_;
        s/\.log\z//;
        rename($old, $_) or warn "rename $old->$_: $!\n"
      }
    }, @ARGV)' ~/tmp

ただしperl、 はFind::File(GNU とは対照的にfind) 安全なディレクトリ トラバーサル³ を行わないので、どちらでも実行したくないことに注意してください/tmp


ノート。

¹ 攻撃者はファイルを作成し、それを見つけて移動させる/tmp/. /auth.log間に(そしてそのウィンドウは簡単に任意の大きさにすることができます)、ディレクトリをシンボリックリンクに置き換えて、名前を次のように変更することができます。findmv"/tmp/. "/var/log/var/log/auth.log/var/log/auth

² さらに悪いことに、攻撃者は/tmp/foo.log悪意のあるシンボリックリンクを作成し、そのcrontabを移動させる可能性があります。crontab/tmp/foo/etc/cron.dの中へ /etc/cron.d. (にも少なくともmv当てはまる)は、両方に当てはまるという曖昧さです。cplnへ引っ越すそしてに引っ越すGNUはmvこれを-tの中へ) そして-T) オプションを選択します。

³ はFile::Findを実行してディレクトリをトラバースしますchdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")...。そのため、誰かが/tmp/foo/barディレクトリを作成し、適切なタイミングでその名前を に変更して、 に移動すること/tmp/barができます。chdir("../..")/

答え2

ここにワンライナーがあります:

find /tmp -name "*.log" -type f -exec sh -c 'f="{}"; mv "$f" "${f%.*}"' \;

シェルを起動し、シェル内の適切な変数に {} を割り当ててから、シェル構文を使用して文字列操作を適用します。

関連情報