Excluir pastas que não correspondem a uma lista

6

Eu preciso de exemplo prático como se livrar de pastas que não estão na lista no Linux. Então eu não preciso comparar seu conteúdo ou md5sums, apenas compare os nomes das pastas.

Por exemplo, uma pasta tem poucas pastas dentro

target_folder/
├── folder1
├── folder2
├── folder3
└── folder4

e minha lista de nomes de pastas é um arquivo txt, inclui folder1, folder2 e não folder3 e folder4.

Como remover folder3 e folder4 via script bash?

Isso foi respondido no serverfault como

GLOBIGNORE=folder1:folder2
rm -r *
uset GLOBIGNORE

mas minha tarefa real é excluir várias pastas. A lista de txt contém cerca de 100 pastas e a pasta de destino para limpeza é de 200 pastas.

Note que isso deve funcionar tanto no Linux quanto no FreeBSD.

EDITAR: target_folder pode conter pastas com subpastas e também arquivos. Nenhum espaço, ponto e nome principal não são semelhantes: foo.com bar.org emptydir file.txt simplefile. Mas todos esses itens devem ser excluídos, exceto os nomes na lista.

A primeira resposta é mais óbvia e simples. O segundo mais avançado e flexível, permite que você apague com base no tipo de item também.

    
por Demontager 21.03.2015 / 05:32

7 respostas

5

Supondo que os nomes dos seus arquivos não contenham :\[*? , você ainda pode usar GLOBIGNORE . Apenas formate sua lista de diretórios de acordo. Por exemplo:

$ cat names.txt
folder1
folder3

Isso é muito fácil de transformar em uma lista separada por dois pontos:

$ paste -s -d : names.txt
folder1:folder3

Então, agora você pode definir isso como o valor de GLOBIGNORE:

GLOBIGNORE=$(paste -s -d : ../names.txt)

E continue a eliminá-los normalmente:

rm -r -- *

Acabei de testar isso no Linux com 300 diretórios e funcionou perfeitamente.

    
por 21.03.2015 / 19:07
3

Outra solução que corresponde ao início e ao fim dos nomes das pastas:

IFS=$'\n'

folders="""folder1
folder2"""

for folder in *; do
    found=0
    if [ -d "$folder" ]; then
        for i in $folders; do
            if [ $(echo "$folder" | grep -c -E "^$i\$") -eq 1 ]; then
                found=1
            fi
        done
        if [ $found -ne 1 ]; then
            echo "$folder found and deleted."
            rm -rf "$folder"
        fi
    fi
done
    
por 12.09.2016 / 16:18
2

Uma abordagem simples é listar todos os arquivos e ignorar os que estão no arquivo de lista. Observe que eu organizo a variável ignore_list para iniciar e terminar com uma nova linha, para que eu possa verificar se $x está incluído de forma simples (apenas verificar que $ignore_list contém $x não funciona, já que também corresponderia arquivos cujo nome é uma subseqüência de um elemento da lista de ignorados).

newline='
'
ignore_list="$newline$(cat list.txt)$newline"
cd target_folder
for x in * .[!.]* ..?*; do
  case "$x" in *"$newline"*) continue;; esac   # sanity check: if the file name contains a newline, leave it alone
  case "$ignore_list" in
    *"$newline$x$newline"*) echo "Skipping $x";;
    *) rm -rf -- "$x";
  esac
done
    
por 21.03.2015 / 22:23
2

Que tal isso?

find ./target_folder/ \
    -mindepth 1 \
    -maxdepth 1 \
    -type d \
    -not -name 'anything[0-9]*' \
    -exec rm -rf {} \;

Deixe-me explicar:

  • -mindepth 1 garante que você não corresponde a ./target_folder/
  • -maxdepth 1 garante que você não corresponde a nenhuma subpasta.
  • -type d informa ao find para corresponder apenas aos diretórios e não aos arquivos.
  • -not -name 'anything[0-9]*' exclui do padrão qualquer coisa que corresponda ao padrão. Bastante óbvio, aquele.
  • -exec deve ser usado com cuidado, especialmente quando envolver rm . Você deve sempre testar isso usando echo antes de prosseguir e rm -rf ing coisas dessa maneira. find pode ser complicado para
  • Não se esqueça do trailing \; ao usar exec.

Você pode ler a página de manual find para mais informações. É uma ferramenta extremamente útil .

    
por 08.04.2015 / 16:51
1

Você pode fazer isso no bash como

for folder in /target_folder/*/
do
    folder=${folder%/}
    if ! grep -qx "${folder##*/}" folders_list.txt
    then
        rm -rf "$folder"
    fi
done
    
por 21.03.2015 / 11:57
1

Com zsh (observe que GLOBIGNORE é específico do bash, nem bash nem zsh são instalados por padrão no FreeBSD, mas ambos estão disponíveis):

(cd /target_folder || exit
all_dirs=(*(/))
to_exclude=(${(f)"$(<names.txt))"})

rm -rf -- ${all_dirs:|to_exclude})

Os recursos zsh -específicos:

  • %código%. *(/) é um qualificador glob que significa apenas selecionar os arquivos do tipo diretório .
  • (/) : expande para o conteúdo (menos os caracteres de nova linha à direita) de $(<file) (vem de file , ksh também tem, mas bifurca um processo para ler o conteúdo de bash ).
  • %código%. É um indicador de expansão de parâmetro que divide a expansão file nos feeds de linha. Com ${(f)...} , você precisaria fazer algo como: "$(...)" .
  • bash expande para os elementos da matriz IFS=$'\n'; set -f; to_exclude=($(<names.txt)) que não estão também na matriz ${A:|B} .
por 13.09.2016 / 11:48
0

Em um sistema GNU ou FreeBSD, você pode fazer:

cd /target_folder &&
  printf '%s
cd /target_folder &&
  printf '%s%pre%' * | grep --null -vxFf names.txt | xargs -r0 rm -rf
' * | grep --null -vxFf names.txt | xargs -r0 rm -rf
    
por 13.09.2016 / 11:56