para loop em pastas com o caractere \ n em seus nomes

9

Eu tenho algumas pastas com \n com seus nomes.

por exemplo:

$ ls
''$'\n''Test'

Isso se refere a uma pasta com nome de teste e uma linha vazia antes de seu nome.

Então, quando eu executo alguns scripts como esse, no diretório pai:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

Mostra:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

Porque é executado duas vezes, uma vez em \n e a outra em Test , porque o nome da pasta tem duas linhas.

Então, como posso resolver esse problema de tal forma que o script saiba que \nTest é apenas uma pasta?

    
por Tara S Volpe 05.06.2018 / 00:21

3 respostas

12

Você tem apenas um comando lá, então basta chamar find com -exec flag chamando rmdir :

find -depth -type d -exec rmdir {} \;

Ou use a opção -delete como em find -type d -delete , mas não funcionará com diretórios não vazios. Para isso, você também precisará do -empty flag. Observe também que -delete implica -depth , portanto, pode ser ignorado. Assim, outra alternativa viável que mantém tudo como um processo:

find -type d -empty -delete

Se o diretório não estiver vazio, use rm -rf {} \; . Para isolar apenas diretórios com \n no nome do arquivo, podemos combinar as citações ANSI-C do bash $'...' com -name opption:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly, nós poderíamos lidar dessa maneira:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

Vale ressaltar que se seu objetivo é a remoção de diretórios, então -delete é suficiente, no entanto, se você quiser executar um comando no diretório, então -exec é o mais apropriado.

Veja também

por Sergiy Kolodyazhnyy 05.06.2018 / 00:40
10

Você poderia usar globs de shell em vez de find :

for d in */ ; do 
    rmdir "$d"
done

O shell glob */ corresponde a todas as pastas no diretório atual. Essa construção for-loop cuida da divisão correta de palavras automaticamente.

Observe que, dependendo das opções do shell, isso pode ignorar pastas ocultas (nome começando com a.). Esse comportamento pode ser alterado para corresponder a todos os arquivos da sessão atual usando o comando shopt -s dotglob .

Também não se esqueça de sempre citar suas variáveis.

    
por Byte Commander 05.06.2018 / 00:36
6

Ambas as respostas escritas até agora chamam rmdir uma vez por diretório, mas como rmdir pode receber vários argumentos, pergunto-me: não existe uma maneira mais eficiente?

Pode-se simplesmente fazer

rmdir */

e essa é definitivamente a maneira mais fácil e eficiente, mas pode gerar um erro no caso de muitos diretórios (veja Qual é o comprimento máximo dos argumentos da linha de comando no gnome-terminal? ). Se você quiser que essa abordagem funcione recursivamente, ative a opção globstar shell com shopt -s globstar e use **/*/ em vez de */ .

Com o% GNUfind (e se não quisermos usar apenas -delete ), poderíamos fazer

find -depth -type d -exec rmdir {} +

que constrói a linha de comando "da mesma maneira que xargs constrói suas linhas de comando" ( man find ). Substitua -depth por -maxdepth 1 se você não quiser que ele funcione de forma recursiva.

Um terceiro e brilhante caminho da IMO é explicado pela steeldriver em esta resposta :

printf '%s
rmdir */
' */ | xargs -0 rmdir

Isso usa o shell embutido printf para criar uma lista de argumentos delimitados por zero, essa lista é então canalizada para xargs , que chama rmdir exatamente com a frequência necessária. Você pode fazê-lo funcionar recursivamente com shopt -s globstar e **/*/ em vez de */ como acima.

    
por dessert 05.06.2018 / 10:33