Como verificar se existem vários arquivos?

5

Eu tenho um script de shell que procura por um arquivo, /tmp/bbsnode1 , e se a existência desse arquivo for verdadeira, ele será excluído. O que eu gostaria de fazer é se existirem vários arquivos ( /tmp/bbsnode2 , /tmp/bbsnode3 e /tmp/bbsnode4 ), excluir todos eles. Mas exclua-os apenas se todos eles existirem.

Aqui está o que eu tenho até agora:

if [ -f /tmp/bbsnode1 ]
then
/usr/bin/rm /tmp/bbsnode1
fi
    
por ignatius 04.11.2016 / 07:03

5 respostas

11

Eu usaria uma função de shell para isso, em vez de um script:

rm-all-or-none() {
  for f; do
    [ -f "$f" ] ||
      { printf '%s is not an existing file, no files removed\n' "$f" >&2
        return 1;}
  done
  rm -fv -- "$@"
}

Então, eu chamaria isso usando a expansão de contraventamento, em vez de uma glob. Globs só expandem para arquivos que existam , mas neste caso queremos especificar os arquivos e somente removê-los se todos deles existirem:

rm-all-or-none /tmp/bbsnode{1..4}

Versão equivalente mais longa:

rm-all-or-none() {
  for f in "$@"; do
    if [ -f "$f" ]; then
      :
    else
      printf '%s is not an existing file, no files removed\n' "$f" >&2
      return 1
    fi
  done
  rm -fv -- "$@"
}

Veja também:

por 04.11.2016 / 07:16
8

Você pode usar um comando arbitrário como ls para verificar os arquivos e excluí-los em uma linha

ls /tmp/bbsnode{1,2,3,4} &>/dev/null && rm /tmp/bbsnode{1,2,3,4}

Observe que, em geral, não é seguro fazer essas coisas em / tmp porque qualquer outro usuário pode criar arquivos conflitantes com os mesmos nomes.

Uma breve explicação:

O valor de retorno de ls é diferente de zero se um dos arquivos não existir. A {1,2,3,4} é a expansão de contraventamento: ela é expandida para uma string para cada número: então /tmp/bbsnode{1,2,3,4} é o mesmo que /tmp/bbsnode1 /tmp/bbsnode2 /tmp/bbsnode3 /tmp/bbsnode4 . O && executará o próximo comando somente se o comando anterior tiver um valor de retorno zero e, portanto, aqui rm será executado apenas se todos os 4 arquivos existirem. Finalmente, o &> /dev/null suprime a saída de ls ( &> redirecionado ambos stdout e stderr , /dev/null se livra dele).

Abaixo de outra solução apenas com builtins de shell. É semelhante ao que os outros responderam, mas sem uma função ou script extra:

set -- /tmp/bbsnode{1,2,3,4}
(for f; do test -f "$f" || exit; done) && rm -- "$@"
    
por 04.11.2016 / 09:27
3

Neste caso específico, você pode fazer:

set -- file[1-4]
[[ $# -eq 4 ]] && rm -f -- "$@"

Esta set s lista de argumentos para os nomes de arquivos que correspondem a file1 , file2 , file3 ou file4 1 e rm s apenas esses arquivos se o número de argumentos for igual a 4, isto é, se todos os arquivos existirem.

1: para simplificar, uso file em vez de /tmp/bbsnode

    
por 04.11.2016 / 19:51
3

A maneira mais simples é através de dois comandos: set -e e stat

#!/bin/bash
set -e # make script exit if there's errors

main()
{
    stat "$@"
    rm "$@"
}

main "$@"

Como isso funciona

A chave aqui é set -e . Isso faz com que o script seja encerrado imediatamente se forem encontrados erros. stat assume a lista de todos os arquivos que você fornece. Se houver um arquivo ausente, ele retornará o status de saída 1, o que significa erro. Dessa forma, seu script chegará a rm part se e somente se não houver erros encontrados em stat

Notas laterais

  • você pode usar stat "$@" > /dev/null para suprimir a saída para a tela
  • A utilização da função main() não é obrigatória, isto é apenas uma preferência estilística do autor.
  • como o rudimeier apontou nos comentários, você também pode usar um one-liner com estes dois comandos: stat "$@" && rm "$@" .

Execução de teste:

bash-4.3$ chmod +x remove_all_if_exist.sh 
bash-4.3$ touch /tmp/{file1,file2}
bash-4.3$ ls /tmp/file*
/tmp/file1  /tmp/file2
bash-4.3$ ./remove_all_if_exist.sh  /tmp/file1 /tmp/file2 /tmp/file3
  File: '/tmp/file1'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d  Inode: 3423307     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ xieerqi)   Gid: ( 1000/ xieerqi)
Access: 2016-11-04 17:44:06.587438784 -0600
Modify: 2016-11-04 17:44:06.587438784 -0600
Change: 2016-11-04 17:44:06.587438784 -0600
 Birth: -
  File: '/tmp/file2'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d  Inode: 3423308     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ xieerqi)   Gid: ( 1000/ xieerqi)
Access: 2016-11-04 17:44:06.587438784 -0600
Modify: 2016-11-04 17:44:06.587438784 -0600
Change: 2016-11-04 17:44:06.587438784 -0600
 Birth: -
stat: cannot stat '/tmp/file3': No such file or directory
bash-4.3$ ls /tmp/file*
/tmp/file1  /tmp/file2
bash-4.3$ ./remove_all_if_exist.sh  /tmp/file1 /tmp/file2
  File: '/tmp/file1'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d  Inode: 3423307     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ xieerqi)   Gid: ( 1000/ xieerqi)
Access: 2016-11-04 17:44:06.587438784 -0600
Modify: 2016-11-04 17:44:06.587438784 -0600
Change: 2016-11-04 17:44:06.587438784 -0600
 Birth: -
  File: '/tmp/file2'
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d  Inode: 3423308     Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/ xieerqi)   Gid: ( 1000/ xieerqi)
Access: 2016-11-04 17:44:06.587438784 -0600
Modify: 2016-11-04 17:44:06.587438784 -0600
Change: 2016-11-04 17:44:06.587438784 -0600
 Birth: -
bash-4.3$ ls /tmp/file*
ls: cannot access '/tmp/file*': No such file or directory
bash-4.3$ 

Alternativa com python:

python -c 'import sys,os;f=sys.argv[1:];c=[os.path.exists(a) for a in f]; l = len(c)*[True]; result = [os.unlink(a) for a in f] if l == c else False; print result' /tmp/file1 /tmp/file2
  • obtenha uma lista de argumentos de linha de comando com sys.argv[1:]
  • crie uma lista de valores true-false se existirem arquivos
  • se todos os arquivos retornarem True, desvincule-os (removê-los)
por 05.11.2016 / 00:49
0

Você pode usar esse script chamado removing_group

#!/usr/bin/bash

function check {
    while (( "$#" )) ; do
        if [ -f "$1" ] ; then
            # echo "The file $1" ;
            shift ;
        else
            # echo "The's no file $1";
            return 1
        fi
    done
    return 0
}

if check $@; then
    while (( "$#" )) ; do
        # echo "Remove $1" ;
        rm "$1" ;
        shift ;
    done
fi

A função check verifica todos os argumentos como arquivos regulares. Se o próximo arquivo não existir, a função retornará 1 e nada acontecerá. Se todos os arquivos existirem, então, o retorno 0 processará if check $@ , o que removerá arquivos um por um. Os arquivos para verificação e remoção descritos como parâmetros na linha de comando, por exemplo

./removing_group /tmp/bbsnode1 /tmp/bbsnode2 /tmp/bbsnode3 /tmp/bbsnode4

ou

./removing_group /tmp/bbsnode{1,2,3,4}
    
por 04.11.2016 / 20:18