Manipulieren Sie {} Rückgabestring von find -exec

Manipulieren Sie {} Rückgabestring von find -exec

Ich möchte es so effizient wie möglich machen, falls es viele Dateien gibt. Ich möchte alle gefundenen Dateien umbenennen und ihre Endung entfernen.

Zum Beispiel:

[/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

Das funktioniert nicht. Wenn es eine normale Bash-Variable wäre, ${var%.*}wäre sie varbis zum letzten zurückgegeben worden ..

Antwort1

Starten Sie eine Shell, um Shell-Parametererweiterungsoperatoren zu verwenden:

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

/tmpBeachten Sie, dass Sie dies nicht in einem für andere beschreibbaren Verzeichnis tun sollten , da dies böswilligen Benutzern ermöglichen würde, Sie dazu zu veranlassen, beliebige .logDateien im Dateisystem¹ umzubenennen (oder Dateien in ein beliebiges Verzeichnis² zu verschieben).

Bei einigen findund mvImplementierungen können Sie find -execdirund verwenden, mv -Tum die Sicherheit zu erhöhen:

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

Oder verwenden Sie rename(die Perl-Variante), die nur einen Systemaufruf ausführt rename()und nicht versucht, Dateien in andere Dateisysteme oder Verzeichnisse zu verschieben …

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

Oder machen Sie das Ganze in perl:

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

Beachten Sie jedoch, dass perl( Find::Fileim Gegensatz zu GNU find) kein sicheres Durchlaufen von Verzeichnissen³ möglich ist. Dies ist also auch bei diesen nichts, was Sie tun möchten /tmp.


Anmerkungen.

¹ Ein Angreifer kann eine /tmp/. /auth.logDatei erstellen und zwischen finddem Suchen und mvVerschieben (und dieses Zeitfenster kann leicht beliebig groß gemacht werden) das "/tmp/. "Verzeichnis durch einen symbolischen Link ersetzen, was /var/logzur /var/log/auth.logUmbenennung in/var/log/auth

² Viel schlimmer, ein Angreifer kann beispielsweise eine /tmp/foo.logbösartige Datei und einen symbolischen Link zu dieser Crontab erstellen und Sie dazu bringen, diese zu verschiebencrontab/tmp/foo/etc/cron.dhinein /etc/cron.d. Das ist die Mehrdeutigkeit mit mv(gilt auch für cpund lnzumindest), die beides sein kannziehen nachUndverschieben in. GNU mvbehebt das Problem mit seinem -t(hinein) Und -T(Zu) Optionen.

³ File::Finddurchläuft das Verzeichnis, indem es Folgendes ausführt chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..").... So kann jemand ein /tmp/foo/barVerzeichnis erstellen und es im richtigen Moment in umbenennen, /tmp/barsodass chdir("../..")Sie in landen /.

Antwort2

Hier ist ein Einzeiler:

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

Es startet eine Shell, weist die {} einer geeigneten Variablen innerhalb der Shell zu und wendet dann die Zeichenfolgenmanipulationen mithilfe der Shell-Syntax an.

verwandte Informationen