Você está arriscando a perda de dados ao usar este curinga do Linux errado
Os curingas do Linux permitem digitar um único comando que atua em grupos inteiros de arquivos ao mesmo tempo. Isso economiza muito tempo, a menos que algo dê errado. E eles podem. Destrutivamente.
Para que servem os curingas
Os curingas mais conhecidos são o ponto de interrogação, ?, e o asterisco, *. Eles podem ser usados para criar padrões de nome de arquivo. O ponto de interrogação representa qualquer caractere único e o asterisco representa qualquer sequência de caracteres, incluindo zero caracteres.
Sabendo disso, podemos construir padrões que correspondam a vários nomes de arquivos. Em vez de digitar todos os nomes de arquivos na linha de comando, digitamos o padrão. Todos os arquivos que correspondem ao padrão são acionados pelo comando.
Se tivermos uma coleção de arquivos em um diretório como este:
Podemos selecionar grupos de arquivos que correspondam aos padrões que fornecemos.
ls taf_*
Isso nos dá todos os arquivos com “taf_” no início de seus nomes.
ls *.sh
ls s*.sh
O primeiro comando lista todos os arquivos de script de shell no diretório. O segundo comando lista apenas arquivos que começam com “s” que também são arquivos de script de shell.
Tudo isso parece bastante simples, e com ls é. Mas outros comandos podem fazer uso desse tipo de correspondência de padrões. Os problemas surgem quando o shell tenta ajudar combinando padrões antes que o comando tenha uma chance.
Usando o Asterisk com o comando find
A ação de expandir um padrão em uma lista de arquivos correspondentes é chamada de globbing.
Começou como um comando independente no Unix versão 6, depois se tornou uma biblioteca que poderia ser vinculada a outros programas e hoje é um shell embutido. A expansão do padrão é executada pelo shell e os resultados da expansão são passados para o comando como parâmetros de linha de comando.
Veremos dois exemplos usando o comando find. Um faz o que você espera, mas o segundo pode surpreendê-lo.
Para este exemplo, usaremos um diretório com um único arquivo, chamado readme.txt. Existem dois diretórios, src e inc. Eles contêm uma mistura de arquivos C, H, MD e TMP.
ls -R
Podemos usar find para encontrar recursivamente arquivos (-type f) com nomes que correspondam ao nosso padrão (-name *.c), fornecendo-nos uma lista dos arquivos C.
find . -type f -name *.c
Podemos adicionar a opção -not para inverter a busca, mostrando-nos tudo exceto os arquivos C.
find . -type f -not -name *.c
Depois de revisar esta lista, optamos por excluir tudo, exceto os arquivos C. Podemos fazer isso adicionando a opção -delete.
find . -type f -not -name *.c -delete
find .
O segundo comando find lista recursivamente tudo dentro e abaixo do diretório atual. Tudo o que resta são nossos arquivos C.
Isso funcionou da maneira que a maioria de nós esperaria. Agora faremos exatamente a mesma coisa, mas desta vez o arquivo no diretório atual não é um arquivo de texto, é um arquivo C.
ls -R
Usaremos o mesmo comando find e opções para excluir tudo, exceto os arquivos C. Não era isso que queríamos.
find . -type f -not -name *.c -delete
find .
Isso excluiu alegremente todos os arquivos da árvore de diretórios, exceto o arquivo C no diretório atual.
Redefiniremos os arquivos mais uma vez e emitiremos o comando da maneira que deveríamos usá-lo.
Todos os arquivos estão no lugar e temos um arquivo C no diretório atual, assim como fizemos antes.
ls -R
Desta vez, colocaremos o padrão curinga entre aspas simples.
find . -type f -not -name '*.c' -delete
find .
Isso é o que queríamos. Tudo se foi em nossos arquivos C.
OK, então o que deu errado?
As aspas simples impedem que o shell expanda o padrão de nome de arquivo. É passado para o comando ou programa como está, para que o comando atue.
No exemplo que funcionou, tínhamos um arquivo readme.txt no diretório atual. O shell não conseguiu encontrar uma correspondência para *.c, então passou *.c para encontrar para agir.
No exemplo que excluiu tudo menos os arquivos C, tínhamos um arquivo chamado main.c no diretório atual. O shell correspondeu ao padrão desse arquivo e passou o nome do arquivo para o comando find. Portanto, as instruções do find eram para excluir tudo que não fosse chamado de main.c.
Podemos ilustrar isso com um pequeno programa em C que nada mais faz do que exibir seus parâmetros de linha de comando na janela do terminal.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
printf("You supplied %d arguments.\n", argc-1);
for (i=1; i<argc; i++)
printf("%-2d) \"%s\"\n", i, argv[i]);
exit (0);
}
Salvei isso como um arquivo chamado glob.c e compilei com:
gcc -o glob glob.c
A variável argc contém o número de argumentos que passamos para o programa. Um loop for percorre a lista de argumentos e imprime cada um deles na janela do terminal.
O loop for começa no argumento um, não em zero. Existe um argumento zero. Ele sempre contém o nome do próprio binário. Para evitar turvar a água, evitei imprimi-la. Os únicos argumentos impressos são aqueles que fornecemos na linha de comando.
./glob one two 3 ant beetle cockroach
Vamos tentar isso com *.c como parâmetro de linha de comando.
ls *.c
./glob *.c
Sem nenhum arquivo C no diretório atual, o shell passa *.c para o comando find. O comando find então atua sobre o próprio padrão curinga. Mas, quando temos um arquivo C no diretório atual, o shell passa o nome do arquivo C correspondente para o programa.
ls *.c
./glob *.c
Nosso programa recebe como parâmetro o nome do arquivo C, e o mesmo vale para o comando find. Então, na verdade, find estava fazendo o que foi dito: excluir todos os arquivos, exceto o arquivo main.c.
Desta vez, colocaremos o padrão curinga entre aspas simples.
ls *.c
./glob '*.c'
O shell ignora a chance de aplicar seu globbing ao padrão curinga e o passa diretamente ao comando para processamento posterior.
Uma solução simples, você pode me citar
Como regra geral, cite padrões curinga que você está passando para comandos como find. Isso é tudo o que é necessário para evitar esse tipo de acidente potencialmente desastroso.