Como verifico se o arquivo existe no Makefile para que eu possa excluí-lo?

135

Na seção limpa do meu Makefile, estou tentando verificar se o arquivo existe antes de excluir permanentemente. Eu uso esse código, mas recebo erros.

O que há de errado com isso?

 if [ -a myApp ]
 then
     rm myApp
 fi

Recebo esta mensagem de erro

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2
Abruzzo Forte e Gentio
fonte
O myApp é uma variável ou um nome de arquivo real?
BugFinder
myApp é para myApplication, ou seja, o nome do arquivo no processo de compilação.
Abruzzo Forte e Gentile
5
Se você apenas deseja evitar fazer paradas se o arquivo não existir, rm -rf myApppode ser uma alternativa. Ou precedendo o comando com um dash ( -rm myApp) para fazer make ignorar o erro da rm (no entanto, imprimirá uma mensagem feia).
precisa saber é o seguinte
1
Seu problema era que trata cada linha de uma regra como um comando separado e as envia individualmente para o shell. É como executar apenas `if [-a myApp] 'por si só. Se você receber esse erro, precisará de uma solução que junte as linhas em uma (usando) ou que termine com cada linha independente da outra. Agora existem vários desses abaixo.
Michael

Respostas:

40

A segunda resposta principal menciona ifeq , no entanto, não menciona que eles devem estar no mesmo nível do nome do destino, por exemplo, para baixar um arquivo apenas se ele não existir atualmente, o seguinte código pode ser usado:

download:
ifeq (,$(wildcard ./glob.c))
    curl … -o glob.c
endif

# THIS DOES NOT WORK!
download:
    ifeq (,$(wildcard ./glob.c))
        curl … -o glob.c
    endif
cnst
fonte
Não funcionou até que eu adicionasse barra invertida `` after if fi
Taras Matsyk
3
Essa resposta será um pouco estranha; a verificação do arquivo acontece quando o makefile é processado, mas a ação ocorre mais tarde quando o destino é criado pelo make. Se você excluir o arquivo enquanto isso, o arquivo não será criado. Eu coloquei uma edição para torná-la mais clara.
Michael
Muito obrigado! Este ponto não ficou claro ao ler o manual.
Kevin Buchs
207

É estranho ver tantas pessoas usando scripts de shell para isso. Eu estava procurando uma maneira de usar a sintaxe nativa do makefile, porque estou escrevendo isso fora de qualquer destino. Você pode usar a wildcardfunção para verificar se o arquivo existe:

 ifeq ($(UNAME),Darwin)
     SHELL := /opt/local/bin/bash
     OS_X  := true
 else ifneq (,$(wildcard /etc/redhat-release))
     OS_RHEL := true
 else
     OS_DEB  := true
     SHELL := /bin/bash
 endif 

Atualizar:

Eu encontrei uma maneira que realmente está funcionando para mim:

ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif
holms
fonte
4
tentei isso, mas eu continuo recebendoMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n
1
Grande uso de curinga, para que possa ser feito com o próprio makefile. Neat :)
anoop
3
Ajuda também a entender o que $(wildcard pattern)realmente faz. Veja o link .
Dr. Dan
1
Mais conciso:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - reinstate monica
2
Vale a pena notar que, se você estiver executando o Windows no cygwin, o uso de curinga dessa maneira faz uma correspondência entre maiúsculas e minúsculas no nome do arquivo; portanto, se o arquivo existir, mas com maiúsculas e minúsculas diferentes do caminho que não será encontrado. Não parece haver nenhuma maneira de substituir esse comportamento no makefile.
Roger Sanders
59

O problema é quando você divide seu comando em várias linhas. Portanto, você pode usar o \no final das linhas para continuação como acima ou pode obter tudo em uma linha com o &&operador no bash.

Então você pode usar um testcomando para testar se o arquivo existe, por exemplo:

test -f myApp && echo File does exist

-f file Verdadeiro se o arquivo existir e for um arquivo regular.

-s file Verdadeiro se o arquivo existir e tiver um tamanho maior que zero.

ou não:

test -f myApp || echo File does not exist
test ! -f myApp && echo File does not exist

o test é equivalente ao [comando.

[ -f myApp ] && rm myApp   # remove myApp if it exists

e funcionaria como no seu exemplo original.

Veja: help [ou help testpara mais sintaxe.

kenorb
fonte
4
Eu teria votado, mas você não avisou que esse -sé um caso especial exists and has a size greater than zero. A pergunta, como está escrita, é independente do tamanho, portanto a existência deve ser verificada usando test -e-se um arquivo ou -dum diretório. Arquivos vazios podem ser especialmente úteis como (por falta de um termo melhor) indicadores / sentinelas, o que pode ser bastante relevante para make.
Underscore_d
Obrigado pela sugestão. Alterado -fpor padrão, pois é mais comum de usar.
Kenorb # 14/15
Como posso acessar o testWindows?
Thorn 4/16
3
Para que isso funcione, é necessário adicionar || trueno final, para que o comando retorne verdadeiro quando o arquivo não existir.
jcubic
2
@AndrewMackenzie test -f myApp || CMD, observe o ||, então se -ffalhar - não existe ( ||), execute o comando. Isso faz sentido?
kenorb
50

Pode ser necessária uma barra invertida no final da linha para continuação (embora talvez isso dependa da versão do make):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;
Mark Wilkins
fonte
2
parece não ser uma sintaxe do Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms
@holms é uma sintaxe do bash. Ao escapar das novas linhas, ele pode ser tratado como um único comando bash. Por padrão, em makeuma nova linha seria um novo comando bash. A principal ressalva disso, além do incômodo de ter muitos \ no final das linhas, é que todo comando deve ser encerrado com o ;que de outra forma estaria implícito no bash.
flungo
1
A resposta certa é por @holms. Nem '\' nem curinga são exatamente destinados a esse propósito. O motivo pelo qual você usaria curinga é a clareza do código. Esse método não é apenas menos legível, é mais propenso a erros de sintaxe.
Maitreya
1
Será que o @holms link fornecido não funciona mais, uso gnu.org/software/make/manual/make.html#Conditionals vez
fero
essa é uma ótima resposta, pois corresponde ao que está errado com o que o questionador original fez e funcionará dependendo da existência do arquivo no momento em que o destino é criado, e não no momento em que o Makefile é iniciado, o que a maioria das pessoas espera e quer na maioria das vezes. Em alguns casos estranhos, a resposta do @cnst seria melhor.
Michael
31

Ou apenas coloque-o em uma linha, como makequiser:

if [ -a myApp ]; then rm myApp; fi;
Jeroen
fonte
essa é uma ótima resposta nesse caso (e deve receber votos positivos), mas não funcionará tão bem se as ações forem mais complexas; nesse caso, mude para o uso de \
Michael
[-a myApp] && rm myApp
Robin Hsu
14

Faltando um ponto e vírgula

if [ -a myApp ];
then
  rm myApp
fi

No entanto, suponho que você esteja verificando a existência antes da exclusão para evitar uma mensagem de erro. Nesse caso, você pode simplesmente usar rm -f myAppquais "forças" serão excluídas, ou seja, não haverá erro se o arquivo não existir.

Drysdam
fonte
1
Isso é exatamente o que eu queria fazer. Muito obrigado.
Abruzzo Forte e Gentile
1
isso não funcionará em um Makefile porque o if ainda está espalhado por várias linhas - você precisa colocá-lo em uma linha ou usar \ es. e mesmo se você adicionou os \ es, ainda falta alguns pontos e vírgulas.
Michael
13
FILE1 = /usr/bin/perl
FILE2 = /nofile

ifeq ($(shell test -e $(FILE1) && echo -n yes),yes)
    RESULT1=$(FILE1) exists.
else
    RESULT1=$(FILE1) does not exist.
endif

ifeq ($(shell test -e $(FILE2) && echo -n yes),yes)
    RESULT2=$(FILE2) exists.
else
    RESULT2=$(FILE2) does not exist.
endif

all:
    @echo $(RESULT1)
    @echo $(RESULT2)

resultados de execução:

bash> make
/usr/bin/perl exists.
/nofile does not exist.
Robin Hsu
fonte
Isso me ajudou, acho que alguns não perceberam que o OP estava perguntando sobre makefile. Não entendi por que "&& echo -n yes" é necessário. Explicação: se os retornos -e teste 1 (não encontrado), então a Shell não irá executar o comando echo, e, portanto, não irá corresponder as sim em ifeq
Brad Dre
Eu acho que você entendeu corretamente em sua declaração de explicação.
Robin Hsu
@BradDre - Altera echo -n yeso sucesso de testna string yessem NL. O ifeqpode então compará-lo com o codificado yes. Tudo porque ifeqdeseja comparar uma string, não um status de sucesso de um comando shell.
Jesse Chisholm
8

Solução de uma linha:

   [ -f ./myfile ] && echo exists

Solução de uma linha com ação de erro:

   [ -f ./myfile ] && echo exists || echo not exists

Exemplo usado nas minhas make cleandiretivas:

clean:
    @[ -f ./myfile ] && rm myfile || true

E make cleansempre funciona sem nenhuma mensagem de erro!

Antonio Feitosa
fonte
3
basta fazer @rm -f myfile. Por causa do sinalizador "-f", "rm" será encerrado com 0, independentemente de o arquivo existir ou não.
Alexey Polonsky
Ou, -rm myfilea indicação do traço principal leva a ignorar qualquer erro.
Jesse Chisholm
1
No meu caso, deixando de fora o || <ação de erro> causou problemas. Seu exemplo final, em que você retornou verdadeiro se o arquivo não existisse, abordou isso muito bem. Obrigado!
Flic
Para mim, o caminho para o meu arquivo precisa ser com apóstrofo ('):@[ -f 'myfile' ] && rm myfile
Sten
0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Se o README.md não existir, não faça nada (e saia com sucesso). Caso contrário, adicione o texto no final."

Se você usar em &&vez de ||, gera um erro quando o arquivo não existe:

Makefile:42: recipe for target 'dostuff' failed
make: *** [dostuff] Error 1
Matt Janssen
fonte
0

Eu estava tentando:

[ -f $(PROGRAM) ] && cp -f $(PROGRAM) $(INSTALLDIR)

E o caso positivo funcionou, mas meu ubuntu bash shell chama isso de VERDADEIRO e quebra na cópia:

[ -f  ] && cp -f  /home/user/proto/../bin/
cp: missing destination file operand after '/home/user/proto/../bin/'

Depois de obter esse erro, pesquiso no Google como verificar se existe um arquivo no make, e esta é a resposta ...

Scott Milano
fonte
0
ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif

Esta solução postada acima funciona melhor. Mas certifique-se de não especificar a atribuição PATH_TO_FILE. Por exemplo,

PATH_TO_FILE = "/usr/local/lib/libhl++.a" # WILL NOT WORK

Deve ser

PATH_TO_FILE = /usr/local/lib/libhl++.a
Om Narasimhan
fonte
-2

Respostas como a de @ mark-wilkins usando \ para continuar linhas e; finalizá-los no shell ou como os do @kenorb alterá-los para uma linha são bons e resolverão esse problema.

há uma resposta mais simples para o problema original (como @ alexey-polonsky apontou). Use o sinalizador -f para rm para que ele não acione um erro

rm -f myApp

isso é mais simples, mais rápido e mais confiável. Apenas tome cuidado para não acabar com uma barra e uma variável vazia

rm -f / $ (myAppPath) #Nunca faça isso

você pode acabar excluindo seu sistema.

Michael
fonte
1
Essa é uma boa resposta para excluir o arquivo que o OP tinha, mas tenho certeza de que a maioria das pessoas que encontra essa pergunta não está realmente procurando excluir nenhum arquivo; isso é realmente evidenciado pelo fato de que a maioria das respostas nem sequer menciona rm; BTW, eu tenho certeza que isso rm -f /$(myAppPath)também não causará danos, porque /é um diretório e -restá faltando.
CNST
Esta não é uma solução mais simples. Como o @cnst disse, pode haver razões pelas quais o pôster original não quer simplesmente fazer um rm -f, por exemplo, eles podem querer rmuma variável.
Dan Nguyen