特定の拡張子を持つファイルを削除するためにディレクトリを再帰的にループする方法


157

ディレクトリを再帰的にループし、拡張子が.pdfとのすべてのファイルを削除する必要があります.doc。私はディレクトリを再帰的にループするように管理していますが、上記のファイル拡張子を持つファイルをフィルターにかけることはできません。

これまでの私のコード

#/bin/sh

SEARCH_FOLDER="/tmp/*"

for f in $SEARCH_FOLDER
do
    if [ -d "$f" ]
    then
        for ff in $f/*
        do      
            echo "Processing $ff"
        done
    else
        echo "Processing file $f"
    fi
done

どこにも行かないので、コードを完成させるのに助けが必要です。


68
理解せずにコードを実行するのは悪いことですが、bashスクリプトを学ぶために多くの人がこのサイトにアクセスしています。私は、「再帰的にbashのスクリプトファイルを」グーグルでここに来て、ほとんどこれらの答えの一つは、それがファイルを削除するだろう実現することなく(ただ再帰をテストするために)走りました。私rmはOPのコードの一部であることを知っていますが、質問された質問には実際には関係ありません。のように無害なコマンドを使用して回答がフレーズされた方が安全だと思いますecho
キース

ここでは、同様の質問:stackoverflow.com/questions/41799938/...
codeforester

1
@キースは同様の経験を持ち、完全に同意し、タイトルを変更しました
idclev 463035818

回答:


146

find そのために作られました。

find /tmp -name '*.pdf' -or -name '*.doc' | xargs rm

19
または、-deleteオプションを見つけます。
Matthew Flaschen、2011年

28
find ... -print0 | xargs -0 ...生の検索ではなく、常にを使用する必要があります。改行を含むファイル名の問題を回避するためのxargs。
Grumbel、2011年

7
使用するxargsオプションを指定せずにすることはほとんど常に悪いアドバイスであり、これは例外ではありません。find … -exec代わりに使用してください。
Gilles「SO-邪悪なことをやめなさい」

211

mouvicielの回答のフォローアップとして、xargsを使用する代わりに、これをforループとして実行することもできます。特に各反復でもっと複雑なことをする必要がある場合は、xargsが扱いにくいことがよくあります。

for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm $f; done

多くの人がコメントしているので、ファイル名にスペースがある場合、これは失敗します。これを回避するには、IFS(内部フィールドセパレーター)を一時的に改行文字に設定します。これ\[?*は、ファイル名にワイルドカード文字が含まれている場合にも失敗します。ワイルドカード拡張(グロビング)を一時的に無効にすることで、この問題を回避できます。

IFS=$'\n'; set -f
for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm "$f"; done
unset IFS; set +f

ファイル名に改行がある場合、それも機能しません。xargsベースのソリューションの方がよいでしょう。

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -print0 | xargs -0 rm

(エスケープされたブラケットは-print0、両方のor句に適用するためにここで必要です。)

GNUおよび* BSD findには、次の-deleteようなアクションもあります。

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -delete

27
これは、ファイル名にスペースがある場合、期待どおりに機能しません(forループは、空白の検索結果を分割します)。
trev 2013

3
どのようにして空白での分割を回避しますか?私は同様のことを試みていて、このループをめちゃくちゃにする空白があるディレクトリがたくさんあります。
クリスチャン

3
それは非常に役立つ答えですか?
zenperttu 2014

1
@Christian次のような引用符を使用して、空白の分割を修正します: "$(find ...)"。Jamesの回答を編集して表示しました。
マシュー

2
@Matthewあなたの編集は何も修正しませんでした:一意に見つかったファイルがある場合にのみ、実際にはコマンドは機能しました。ファイル名にスペース、タブなどがない場合、少なくともこのバージョンは機能します。古いバージョンにロールバックしました。賢明なことに注意することで、本当に修正できますfor f in $(find ...)この方法は使用しないでください。
gniourf_gniourf 2014年

67

なしfind

for f in /tmp/* tmp/**/* ; do
  ...
done;

/tmp/*はdir /tmp/**/*内のファイルであり、サブフォルダ内のファイルです。globstarオプション(shopt -s globstar)を有効にする必要がある可能性があります。したがって、質問のコードは次のようになります。

shopt -s globstar
for f in /tmp/*.pdf /tmp/*.doc tmp/**/*.pdf tmp/**/*.doc ; do
  rm "$f"
done

これには、bash≥4.0(またはzshなしshopt -s globstar、またはksh set -o globstarありshopt -s globstar)が必要であることに注意してください。さらに、bash <4.3では、これはディレクトリだけでなくディレクトリへのシンボリックリンクも通過するため、通常は望ましくありません。


1
OSXでスペースを含むファイル名を使用しても、この方法は
うまくいきました

2
globstarはBash 4.0以降でのみ利用できることに注意してください。これは多くのマシンのデフォルトバージョンではありません。
Troy Howard

1
最初の引数を指定する必要はないと思います。(少なくとも今日の時点では)for f in /tmp/**十分です。/ tmp dirのファイルを含めます。
phil294 '13年

1
こんな方がいいのでは?for f in /tmp/*.{pdf,doc} tmp/**/*.{,pdf,doc} ; do
Ice-Blaze 2017

1
**素晴らしい拡張ですが、POSIXに移植できませんsh。(この質問はbashとタグ付けされていますが、ここでのいくつかのソリューションとは異なり、これは実際にはBashのみです。または、他のいくつかの拡張シェルでも機能します。)
tripleee

27

何かを再帰的に実行したい場合は、再帰を使用することをお勧めします(そうです、スタックなどを使用して実行できますが、ちょっと)。

recursiverm() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursiverm)
    fi
    rm -f *.pdf
    rm -f *.doc
  done
}

(cd /tmp; recursiverm)

findはいえ、すでに提案されているように、おそらくより良い選択です。


15

シェル(bash)を使用した例を次に示します。

#!/bin/bash

# loop & print a folder recusively,
print_folder_recurse() {
    for i in "$1"/*;do
        if [ -d "$i" ];then
            echo "dir: $i"
            print_folder_recurse "$i"
        elif [ -f "$i" ]; then
            echo "file: $i"
        fi
    done
}


# try get path from param
path=""
if [ -d "$1" ]; then
    path=$1;
else
    path="/tmp"
fi

echo "base path: $path"
print_folder_recurse $path

15

これはあなたの質問に直接答えるものではありませんが、ワンライナーで問題を解決できます:

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -exec rm {} +

find(GNU、BSD)の一部のバージョンには-delete、次の呼び出しの代わりに使用できるアクションがありますrm

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -delete

7

このメソッドはスペースをうまく処理します。

files="$(find -L "$dir" -type f)"
echo "Count: $(echo -n "$files" | wc -l)"
echo "$files" | while read file; do
  echo "$file"
done

1つずつ編集、修正

function count() {
    files="$(find -L "$1" -type f)";
    if [[ "$files" == "" ]]; then
        echo "No files";
        return 0;
    fi
    file_count=$(echo "$files" | wc -l)
    echo "Count: $file_count"
    echo "$files" | while read file; do
        echo "$file"
    done
}

エコー後の「-n」フラグは不要だと思います。自分でテストしてください: "-n"を指定すると、スクリプトは間違った数のファイルを返します。ディレクトリ内のファイルが1つだけの場合、「カウント:0」を出力します
Lopa

1
これは、すべてのファイル名で機能するわけではありません。名前の末尾にスペースがあり、ファイル名に改行が含まれ、一部のファイル名にバックスラッシュが含まれていると失敗します。これらの欠陥は修正される可能性がありますが、アプローチ全体が不必要に複雑であるため、気にする必要はありません。
Gilles「SO-邪悪なことをやめよう」

3

bashの場合(バージョン4.0以降):

shopt -s globstar nullglob dotglob
echo **/*".ext"

それで全部です。
末尾の拡張子 ".ext"は、その拡張子を持つファイル(またはdirs)を選択します。

オプションglobstarは**(再帰検索)をアクティブにします。
オプションnullglobは、どのファイル/ディレクトリとも一致しない場合、*を削除します。
オプションdotglobには、ドットで始まるファイル(隠しファイル)が含まれます。

bash 4.3より前のバージョン**/では、ディレクトリへのシンボリックリンクもトラバースすることに注意してください。これは望ましくありません。


1

次の関数は、ディレクトリ内のすべての\home\ubuntuディレクトリ(ubuntuではディレクトリ構造全体)を再帰的に反復し、必要なチェックをelseブロックで適用します。

function check {
        for file in $1/*      
        do
        if [ -d "$file" ]
        then
                check $file                          
        else
               ##check for the file
               if [ $(head -c 4 "$file") = "%PDF" ]; then
                         rm -r $file
               fi
        fi
        done     
}
domain=/home/ubuntu
check $domain

1

これは、私がこれを行う最も簡単な方法です。 rm **/@(*.doc|*.pdf)

** これを再帰的に行います

@(*.doc|*.pdf) pdfまたはdocで終わるファイルを探します

安全に交換することによってテストする簡単rmls


0

の出力findを別のユーティリティにパイプする理由はありません。find持って-deleteそれに組み込まれた旗を。

find /tmp -name '*.pdf' -or -name '*.doc' -delete

0

提供される他の回答には、で始まるファイルまたはディレクトリは含まれません。以下は私のために働きました:

#/bin/sh
getAll()
{
  local fl1="$1"/*;
  local fl2="$1"/.[!.]*; 
  local fl3="$1"/..?*;
  for inpath in "$1"/* "$1"/.[!.]* "$1"/..?*; do
    if [ "$inpath" != "$fl1" -a "$inpath" != "$fl2" -a "$inpath" != "$fl3" ]; then 
      stat --printf="%F\0%n\0\n" -- "$inpath";
      if [ -d "$inpath" ]; then
        getAll "$inpath"
      #elif [ -f $inpath ]; then
      fi;
    fi;
  done;
}

-1

するだけ

find . -name '*.pdf'|xargs rm

4
いいえ、これを行わないでください。ファイル名にスペースや他の面白い記号が含まれている場合、これは壊れます。
gniourf_gniourf 2014年

-1

以下は、指定されたディレクトリを再帰的にループし、すべてのコンテンツをリストします。

for d in /home/ubuntu/*; do echo "listing contents of dir: $d"; ls -l $d/; done


いいえ、この関数は何も再帰的にトラバースしません。サブディレクトリの内容のみを一覧表示します。それはただの綿毛ls -l /home/ubuntu/*/なので、それはかなり役に立たない。
Gilles「SO-邪悪なことをやめなさい」

-1

コマンドの実行に使用するシェルを変更できる場合は、ZSHを使用してジョブを実行できます。

#!/usr/bin/zsh

for file in /tmp/**/*
do
    echo $file
done

これは、すべてのファイル/フォルダーを再帰的にループします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.