¿Cómo verifico si el archivo existe en Makefile para poder eliminarlo?


135

En la sección limpia de mi Makefile, estoy tratando de verificar si el archivo existe antes de eliminarlo permanentemente. Uso este código pero recibo errores.

¿Qué tiene de malo?

 if [ -a myApp ]
 then
     rm myApp
 fi

Me sale este mensaje de error

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2

¿Es myApp una variable o un nombre de archivo real?
BugFinder

myApp es para myApplication, es decir, el nombre de archivo del proceso de compilación.
Abruzzo Forte e Gentile

55
Si solo desea evitar que se detenga si el archivo no existe, rm -rf myApppodría ser una alternativa. O precediendo el comando con un guión ( -rm myApp) para hacer que ignore el error de rm (sin embargo, imprimirá un mensaje feo).
thomasa88

1
Su problema fue que make trata cada línea de una regla como un comando separado y las envía individualmente al shell. Es como ejecutar solo `if [-a myApp] 'por sí solo. Si obtiene este error, necesita una solución que une las líneas en una (usando) o que termina con cada línea independiente de la otra. Ahora hay varios de estos a continuación.
Michael

Respuestas:


40

La segunda respuesta principal menciona ifeq, sin embargo, no menciona que estos deben estar en el mismo nivel que el nombre del objetivo, por ejemplo, para descargar un archivo solo si actualmente no existe, se podría usar el siguiente código:

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

# THIS DOES NOT WORK!
download:
    ifeq (,$(wildcard ./glob.c))
        curl … -o glob.c
    endif

no funcionó hasta que agregué la barra invertida `` after if fi
Taras Matsyk

3
Esta respuesta será un poco rara; la verificación del archivo ocurre cuando se procesa el archivo MAKE pero la acción sucederá más tarde cuando el destino es creado por make. Si eliminas el archivo mientras tanto, el archivo no se creará. He puesto una edición para que quede más claro.
Michael

¡Muchas gracias! Este punto no quedó claro al leer el manual.
Kevin Buchs

207

Es extraño ver a tanta gente usando scripts de shell para esto. Estaba buscando una manera de usar la sintaxis nativa del archivo MAKE, porque estoy escribiendo esto fuera de cualquier objetivo. Puede usar la wildcardfunción para verificar si el archivo 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 

Actualizar:

Encontré una forma que realmente funciona para mí:

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

44
Makefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Probé

1
Gran uso de comodines, por lo que se puede hacer con el propio archivo MAKE. Neat :)
anoop

3
Ayuda a comprender también lo que $(wildcard pattern)realmente hace. Ver enlace .
Dr. Dan

1
Más conciso:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - reinstalar a monica el

2
Vale la pena señalar si está ejecutando en Windows bajo cygwin, el uso de comodines de esta manera hace una coincidencia entre mayúsculas y minúsculas en el nombre de archivo, por lo que si el archivo existe pero con un caso diferente al de la ruta, no se encontrará. No parece haber ninguna forma de anular este comportamiento dentro del archivo MAKE.
Roger Sanders

59

El problema es cuando divide su comando en varias líneas. Entonces, puedes usar el\ al final de las líneas para continuar como se indica arriba o puede obtener todo en una línea con el&& operador en bash.

Luego puede usar un testcomando para probar si el archivo existe, por ejemplo:

test -f myApp && echo File does exist

-f file Verdadero si el archivo existe y es un archivo normal.

-s file Verdadero si el archivo existe y tiene un tamaño mayor que cero.

o no:

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

El testes equivalente al [comando.

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

y funcionaría como en tu ejemplo original.

Ver: help [o help testpara más sintaxis.


44
Hubiera votado positivamente, pero no advirtió que -ses un caso especial exists and has a size greater than zero. La pregunta tal como está escrita es independiente del tamaño, por lo que la existencia debe verificarse utilizando test -eun archivo o -dun directorio. Los archivos vacíos pueden ser especialmente útiles como (por falta de un término mejor) indicadores / centinelas, que pueden ser bastante relevantes para make.
underscore_d

Gracias por la sugerencia. Cambiado -fpor defecto, ya que es más común de usar.
kenorb

¿Cómo puedo acceder testa Windows?
thowa

3
Para que esto funcione, debe agregar || trueal final para que el comando devuelva verdadero cuando el archivo no existe.
jcubic

2
@AndrewMackenzie test -f myApp || CMD, observe el ||, así que si -ffalla, no existe ( ||), ejecute el comando. ¿Tiene sentido?
kenorb

50

Puede necesitar una barra invertida al final de la línea para continuar (aunque tal vez eso dependa de la versión de make):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;


@holms es una sintaxis bash. Al escapar de las nuevas líneas, permite que se maneje como un solo comando bash. Por defecto en makeuna nueva línea sería un nuevo comando bash. La principal advertencia de esto, aparte de la molestia de tener muchos \ al final de las líneas, es que cada comando debe terminarse con ;lo que de otro modo estaría implícito en bash.
flungo

1
La respuesta correcta es por @holms. Ni '\' ni comodín están destinados exactamente a este propósito. La razón por la que usaría comodines es por la claridad del código. Este método no solo es menos legible, sino que también es más propenso a errores de sintaxis.
Maitreya

1
el enlace proporcionado por @holms ya no funciona, use gnu.org/software/make/manual/make.html#Conditionals en su lugar
fero

esta es una gran respuesta porque coincide con lo que está mal con lo que hizo el interrogador original y funcionará dependiendo de si el archivo existe en el momento en que se construye el objetivo en lugar de en el momento en que se inicia el Makefile, que es lo que la mayoría de la gente esperaría y quiero la mayor parte del tiempo En algunos casos extraños, la respuesta de @cnst sería mejor.
Michael

31

O simplemente póngalo en una línea, como makele gusta:

if [ -a myApp ]; then rm myApp; fi;

esa es una gran respuesta en este caso (y debería obtener votos positivos) pero no funcionará tan bien si las acciones fueran más complejas, en cuyo caso simplemente cambie a usar \
Michael

[-a myApp] && rm myApp
Robin Hsu

14

Falta un punto y coma

if [ -a myApp ];
then
  rm myApp
fi

Sin embargo, supongo que está comprobando la existencia antes de la eliminación para evitar un mensaje de error. Si es así, puede usar rm -f myAppqué "fuerza" eliminar, es decir, no genera errores si el archivo no existe.


1
Eso es exactamente lo que quería hacer. Muchas gracias.
Abruzzo Forte e Gentile

1
esto no funcionará en un Makefile porque el if todavía está extendido en varias líneas; debe poner esto en una línea o usar \ es. e incluso si agregaste \ es todavía te faltan algunos puntos y coma.
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 ejecución:

bash> make
/usr/bin/perl exists.
/nofile does not exist.

Esto me ayudó, creo que algunos extrañaron que el OP preguntara sobre el archivo MAKE. No entendí por qué "&& echo -n yes" es necesario. Explicación: si test -e devuelve 1 (no encontrado), el shell no ejecutará el comando echo y, por lo tanto, no coincidirá con el sí en ifeq
Brad Dre

Creo que lo entiendes correctamente en tu declaración de explicación.
Robin Hsu

@BradDre: echo -n yescambia el éxito de testla cadena yessin NL. El ifeqentonces se puede comparar con el disco codificado yes. Todo porque ifeqquiere una cadena para comparar, no un estado de éxito de un comando de shell.
Jesse Chisholm el

8

Solución de una línea:

   [ -f ./myfile ] && echo exists

Solución de una línea con acción de error:

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

Ejemplo usado en mis make cleandirectivas:

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

¡Y make cleansiempre funciona sin ningún mensaje de error!


3
simplemente haga @rm -f myfile. Debido al indicador "-f", "rm" saldrá con 0 independientemente de si el archivo existe o no.
Alexey Polonsky

O bien, -rm myfileel guión inicial indica que ignore cualquier error.
Jesse Chisholm el

1
En mi caso, dejando fuera el || <acción de error> causó problemas. Su último ejemplo, donde devolvió verdadero si el archivo no existía, lo solucionó muy bien. ¡Gracias!
Flic

Para mí, el camino hacia mi archivo debe ser con apóstrofe ('):@[ -f 'myfile' ] && rm myfile
Sten

0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Si README.md no existe, no haga nada (y salga con éxito). De lo contrario, agregue texto al final".

Si usa en &&lugar de ||generar un error cuando el archivo no existe:

Makefile:42: recipe for target 'dostuff' failed
make: *** [dostuff] Error 1

0

Estaba intentando:

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

Y el caso positivo funcionó, pero mi shell de ubuntu bash llama a esto VERDADERO y se rompe en la copia:

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

Después de recibir este error, busco en Google cómo verificar si existe un archivo en make, y esta es la respuesta ...


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

Esta solución publicada anteriormente funciona mejor. Pero asegúrese de no encadenar la asignación PATH_TO_FILE Por ejemplo,

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

Debe ser

PATH_TO_FILE = /usr/local/lib/libhl++.a

-2

Las respuestas como la de @ mark-wilkins usando \ para continuar líneas y; para terminarlos en el shell o como los de @kenorb, cambiar esto a una línea son buenos y solucionará este problema.

Hay una respuesta más simple al problema original (como señaló @ alexey-polonsky). Use el indicador -f para rm para que no desencadene un error

rm -f myApp

Esto es más simple, más rápido y más confiable. Solo tenga cuidado de no terminar con una barra oblicua y una variable vacía

rm -f / $ (myAppPath) #NUNCA HAGA ESTO

puede terminar eliminando su sistema.


1
Esta es una buena respuesta para eliminar el archivo que tenía el OP, pero estoy bastante seguro de que la mayoría de las personas que encuentran esta pregunta no buscan eliminar ningún archivo; esto se evidencia en el hecho de que la mayoría de las respuestas ni siquiera mencionan rmen absoluto; Por cierto, estoy bastante seguro de que rm -f /$(myAppPath)tampoco hará ningún daño, porque /es un directorio y -rfalta.
Cnst

Esta no es una solución más simple. Como dijo @cnst, puede haber razones por las cuales el póster original no simplemente quiere hacer un rm -f, por ejemplo, puede querer rmuna variable.
Dan Nguyen el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.