Backticks não funcionam ao encapsular o caminho entre aspas

3

Eu tenho um script onde quero limpar arquivos antigos quando há mais de um limite definido.

Eu tenho este comando:

/bin/rm -f '/bin/ls -t $bkup_p/mysql.daily/*  2> /dev/null | /bin/awk 'NR>'5'

que funciona e porque poderia haver um espaço em $ bkup_p, tentei alterá-lo para

/bin/rm -f '/bin/ls -t "$bkup_p/mysql.daily/*"  2> /dev/null | /bin/awk 'NR>'5'

Mas isso não funciona. Não mostra os arquivos em questão, é apenas vazio

    
por Kenneth Fribert 22.10.2016 / 10:25

4 respostas

1

Como outros já disseram, você não deve usar ls para isso. Mas se o seu ambiente não tem nada mais sensato e você conhece as ressalvas , você pode se safar, se seus nomes de arquivos forem bons o suficiente : sem espaço em branco ou caracteres não imprimíveis, e especialmente nada que o shell interprete como um caractere globbing (pelo menos ?*[] ).

Usar algo como o Perl para classificar os arquivos ou colocar as datas nos nomes dos arquivos para que você possa depender da ordem glob seria melhor.

Com isto dito: em seu segundo trecho, o asterisco é citado, então não acontece a globalização. Você veria um erro sobre some dir/mysql.daily/* não existente, mas você não o fez desde que redirecionou a saída do erro.

Nomes de arquivos com espaços lhe darão o problema de que a saída da substituição do comando é dividida ao longo dos espaços, então algo como some dir/foo será dado a rm em duas partes: some e dir/foo . Espero que nenhum deles exista, já que eles serão removidos.

Se apenas o nome do diretório contiver espaços, você poderia apenas cd primeiro. Ou defina IFS para conter apenas uma nova linha (em vez do espaço padrão, tabulação, nova linha). Você mencionou um NAS, então é provável que você tenha o busybox. Isso deve funcionar tanto no busybox quanto no bash, e imprimir os nomes dos arquivos, um por linha:

IFS=$'\n'
printf "%s\n" $( ls -t "$bkup_p/mysql.daily/"* | tail -n +6 )

Substitua printf "%s\n" por rm para remover os arquivos, se tiver certeza de que funciona corretamente.

    
por 22.10.2016 / 21:06
2

O Bash não é muito útil quando se trata de filtrar arquivos no momento da modificação e executar o comando neles.

Eu recomendaria tentar o z-shell, então primeiro execute zsh , e então

rm -i -- "$bkup_p"/mysql.daily/*(DN.Om[1,5])

Isso remove os cinco arquivos simples mais antigos no diretório fornecido, o que eu suspeito ser provavelmente o que você está tentando alcançar. Obviamente, no final do dia, altere rm -i para rm -f , se necessário.

Para remover cinco novos arquivos,

rm -i -- "$bkup_p"/mysql.daily/*(DN.om[1,5])

Agora, como funciona. Tudo dentro de () são chamados qualificadores glob, que basicamente filtram arquivos dependendo de suas necessidades:

  • D inclui dotfiles (aqueles que começam com . )
  • N não reporta erro se não corresponder a
  • . seleciona apenas arquivos simples
  • om ordena o tempo de modificação ( Om ordena na ordem inversa)
  • [1,5] seleciona apenas cinco arquivos da lista

Eu acredito que tudo isso deve funcionar mesmo com caracteres especiais em nomes de arquivos (espaços, novas linhas, etc.)

    
por 22.10.2016 / 11:36
1

Eu recomendaria contra a análise da saída de ls , pois ela não formata adequadamente a saída para o encadeamento em um novo comando e tem problemas de portabilidade.
Eu tentaria algo assim ao invés

find $bkup_p/mysql.daily/ -type f -a -mtime +7 -a -name "*.sql" -a -exec rm -f {} +

nota:

  • "*.sql" altere isso conforme necessário
  • -mtime +7 significa * se este arquivo foi modificado mais (+) do que 7 dias atrás, obviamente mude isso também conforme necessário

No caso de você querer ter sempre os 10 arquivos mais novos - não importa o que (e você tem o GNU find ) você poderia tentar

find $bkup_p/mysql.daily/ -maxdepth 1 -type f -a -printf "%T+\t%p\n" | sort -r | sed -n '10,$p' | awk '{print $2}' | xargs rm -f

para mais informações sobre a formatação de find output com o operador -printf , consulte

man find | less '+/^\s*-printf'
    
por 22.10.2016 / 11:00
0

O problema de * não funcionar é porque, quando você o cita, ele não expande.

Compare os resultados disso: echo * com isso: echo "*" .

A solução robusta é usar uma matriz, um arquivo em cada posição da matriz.

Se você tiver sorte o suficiente e encontrar o suporte a este -printf :

dir="$bkup_p/mysql.daily/"

find "$dir" -printf '%T@:%i\n'

Todos os nomes de arquivos serão evitados. Cada arquivo será uma linha. E uma linha de apenas números, um ponto opcional e dois pontos.

Isso pode ser facilmente classificado:

find "$dir" -printf '%T@:%i\n' | sort -rn

A matriz mais simples é os argumentos posicionais.
Isso irá definir todos os arquivos no diretório para a lista de argumentos posicionais:

set -f; set -- $(find "$dir" -printf '%T@:%i\n' | sort -n )

O set -f evitará a expansão de qualquer nome de arquivo que também seja um wilcard * . Para evitar (remover) os primeiros 6 nomes de arquivos, apenas shift deles:

shift 5

E faça o que você precisa para os arquivos restantes:

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done

O script inteiro.

O script inteiro (Robusto para qualquer nome de arquivo) se torna:

dir="$bkup_p/mysql.daily/"

set -f
set -- $(find "$dir" -type f -printf '%T@:%i\n'|sort -rn)
shift 5

for    f
do     rm -f "$(find "$dir" -inum "${f#*:}")"
done
    
por 24.10.2016 / 05:29