Teste se existem arquivos correspondentes a um padrão para executar um script

28

Estou tentando gravar uma instrução if para testar se há arquivos correspondentes a um determinado padrão. Se houver um arquivo de texto em um diretório, ele deverá executar um determinado script.

Meu código atualmente:

if [ -f /*.txt ]; then ./script fi

Por favor, dê algumas ideias; Eu só quero executar o script se houver um .txt no diretório.

    
por user40952 13.06.2013 / 17:28

8 respostas

39
[ -f /*.txt ]

retornaria true somente se houver um (e apenas um) arquivo não oculto em / cujo nome termine em .txt e se esse arquivo for um arquivo regular ou um symlink para um arquivo regular.

Isso porque os curingas são expandidos pelo shell antes de serem passados para o comando (aqui [ ).

Portanto, se houver um /a.txt e /b.txt , [ receberá 5 argumentos: [ , -f , /a.txt , /b.txt e ] . [ , então, reclama que -f recebe muitos argumentos.

Se você quiser verificar se o padrão *.txt se expande para pelo menos um arquivo não oculto (regular ou não):

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglob é bash específico, mas shells como ksh93 , zsh , yash , tcsh têm instruções equivalentes.

Observe que ele localiza esses arquivos lendo o conteúdo do diretório, ele não tenta acessar esses arquivos, o que o torna mais eficiente do que as soluções que chamam comandos como ls ou stat nessa lista. arquivos computados pelo shell.

O equivalente padrão sh seria:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

O problema é que, com os shells Bourne ou POSIX, se um padrão não corresponder, ele se expande para si mesmo. Então, se *.txt for expandido para *.txt , você não sabe se é porque não há nenhum arquivo .txt no diretório ou porque há um arquivo chamado *.txt . Usando [*].txt *.txt permite discriminar entre os dois.

    
por 13.06.2013 / 17:41
11

Você sempre pode usar find :

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

Explicação:

  • find . : pesquisa no diretório atual
  • -maxdepth 1 : não pesquisa subdiretórios
  • -type f : pesquisa somente arquivos regulares
  • name "*.txt" : pesquisa por arquivos terminados em .txt
  • 2>/dev/null : redireciona as mensagens de erro para /dev/null
  • | grep -q . : grep para qualquer caractere, retornará false se nenhum caractere for encontrado.
  • && ./script : execute ./script apenas se o comando anterior foi bem-sucedido ( && )
por 13.06.2013 / 17:38
7

Aqui está um folheto para fazer isso:

$ ls
file1.pl  file2.pl

arquivos existem

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

arquivos não existem

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

Essa abordagem faz uso dos operadores || e && no bash. Estes são os operadores "ou" e "e".

Portanto, se o comando stat retornar um $? igual a 0, o primeiro echo será chamado, se retornar 1, então o segundo echo será chamado.

retorna resultados do stat

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

Esta questão é amplamente abordada no stackoverflow:

por 13.06.2013 / 17:47
7

Uma possível solução também é Bash incorporado em compgen . Esse comando retorna todas as correspondências possíveis para um padrão de globbing e tem um código de saída indicando se algum arquivo correspondeu.

compgen -G "/*.text" > /dev/null && ./script

Encontrei esta questão enquanto procurava soluções mais rápidas.

    
por 23.11.2016 / 23:05
4

Como Chazelas ressalta, seu script falharia se a expansão de curinga corresponder a mais de um arquivo.

No entanto, há um truque que eu uso ( mesmo eu não gosto muito ) para se locomover:

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

Como funciona?

A expansão de curingas corresponderá a uma matriz de nomes de arquivos, obtemos o primeiro se houver algum, caso contrário, null, se não houver correspondência.

    
por 06.07.2015 / 01:26
1

Simples como:

cnt='ls \*.txt 2>/dev/null | wc -l'
if [ "$cnt" != "0" ]; then ./script fi

wc -l conta as linhas no curinga expandido.

    
por 04.02.2015 / 08:22
0

Eu gosto da solução de array anterior, mas isso pode se tornar um desperdício com um grande número de arquivos - o shell usaria uma grande quantidade de memória para construir o array, e somente o primeiro elemento seria testado.

Aqui está uma estrutura alternativa que testei ontem:

$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no

O valor de saída do grep parece estar determinando o caminho através da estrutura de controle. Isso também testa com expressões regulares em vez de padrões de shell. Alguns dos meus sistemas têm o comando "pcregrep" que permite correspondências de regex muito mais sofisticadas.

(Eu editei esta resposta para remover um "ls" na substituição do comando depois de ler a crítica acima para analisá-la.)

    
por 25.10.2017 / 18:38
-2

se você quiser usar uma cláusula if, avalie a contagem:

if (( 'ls *.txt 2> /dev/null|wc -l' ));then...
    
por 22.05.2016 / 17:15