"command not found" when calling function in bash [duplicate] - bash

This question already has answers here:
How to call a function in shell Scripting?
(7 answers)
Bash - writing function definition in script after first call (as a GOTO/jump problematics)
(3 answers)
How can i check if no arguments are given [duplicate]
(1 answer)
Closed 4 years ago.
I am trying to set up a simple script to compile my project that takes the name of the module(s) to compile, or if no arguments, compile them all.
None of the other answers on StackOverflow have worked for me.
When I try the below script I get this error:
$ ./build.sh updater
Creating the build path: build/0.1.4-master
./build.sh: line 21: updater: command not found
I don't like the ambiguity of the language, it could be a function or a command. But it is what it is. How can I make it execute the function and not try to run a command called updater?
#! /bin/sh -e
# release="0.0.0-$(git rev-parse --abbrev-ref HEAD)"
release="$(git describe --tags --abbrev=0)-$(git rev-parse --abbrev-ref HEAD)"
echo "Creating the build path: build/$release"
mkdir -p "build/$release"
while [ "$#" -gt 0 ]; do
param="$1"
case $param in
s|setup)
setup
shift
;;
l|launcher)
launcher
shift
;;
u|updater)
updater
shift
;;
d|deflector)
deflector
shift
;;
# *)
# setup
# launcher
# updater
# deflector
# shift
# ;;
esac
done
function setup {
echo "Compiling executable: build/$release/setup.exe"
ldc2 "source/setup.d" "source/common.d" -of="build/$release/setup.exe" \
-O3 -ffast-math -release -g
echo "Adding icon to executable: build/$release/setup.exe"
[ -e "build/$release/setup.exe" ] && \
rcedit "build/$release/setup.exe" --set-icon "icons/icon.ico"
}
function launcher {
echo "Compiling executable: build/$release/launcher.exe"
ldc2 "source/launcher.d" "source/common.d" -of="build/$release/launcher.exe" \
-O3 -ffast-math -release -g
echo "Adding icon to executable: build/$release/launcher.exe"
[ -e "build/$release/launcher.exe" ] && \
rcedit "build/$release/launcher.exe" --set-icon "icons/icon.ico"
}
function updater {
echo "Compiling executable: build/$release/updater.exe"
ldc2 "source/updater.d" "source/common.d" -of="build/$release/updater.exe" \
-O3 -ffast-math -release -g
}
function deflector {
echo "Compiling executable: build/$release/deflector.exe"
ldc2 "source/deflector.d" "source/common.d" -of="build/$release/deflector.exe" \
-O3 -ffast-math -release -g
}
echo "Removing residual object files."
find "build/$release" -name "*.obj" -delete
echo "Copying engines file: build/$release/engines.txt"
cp "engines.txt" "build/$release"
echo "Copying libcurl library: build/$release/libcurl.dll"
cp $(which libcurl.dll) "build/$release"
I would also like help making a default case for when there are no arguments passed to the script. My attempt can be seen in the commented block in the switch case.
Appreciate any help.

You need to define the functions before they are called from top-level code.
...
updater () { ... }
...
while [ "$#" -gt 0 ]; do
...
done

Related

Makefile target unexpectadly raising error

My Makefile includes follow target
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then $(error Sorry BIN_FILE is directory); else ./$(BIN_FILE) $(RUN_ARGS); fi
But it raises the error no matter whether test passes or not. What is wrong? Why it raises an error even BIN_FILE is not directory? Have directive $(error... any special meaning?
Many thanks.
You can't embed a Make expression like that inside your shell script. Make performs all expansions of $(...) expressions before the shell even starts, so it sees your $(error ...) command and exits. You would need to emit this error using shell logic instead, doing something like:
run: $(BIN_FILE)
if [ -d $(BIN_FILE) ]; then \
echo "Sorry BIN_FILE is directory"; \
exit 1; \
fi
$(BIN_FILE) $(RUN_ARGS)
Or with slightly more compact logic:
run: $(BIN_FILE)
[ -d "$(BIN_FILE)" ] && { echo "$(BIN_FILE) is a directory"; exit 1; } ||:
$(BIN_FILE) $(RUN_ARGS)

Not able to run if condition inside Makefile

I have the following content in my makefile:
ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH))
all: build
build:
echo "Doing the build"
if [ "$$ARCH" = "amd64" ]; then \
touch test; \
echo "inside the condition loop"; \
fi
Now, while running make command, I get the following output:
# make
echo "Doing the build"
Doing the build
if [ "$ARCH" = "amd64" ]; then \
touch test; \
echo "inside the condition loop"; \
fi
This executes, but it doesn't create the test file in the current directory.
# ls
Makefile
Any idea what am I doing wrong here? Or any tips to debug further? TIA.
By default make variables are not exported. You probably want to add 'export ARCH' after the assignment.
ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH))
export ARCH
# OR
export ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH))
For the build action to work you need ARCH to be export for two reason
The '[ "$ARCH" = "amd64" ]; refer to environment variable ARCH
The actual build will probably need ARCH set correctly to work.

Makefile:33: *** missing separator despite of having no spaces

I am getting the following error in my makefile despite of having no spaces on that line, I edited a part of the file by adding tabs. But on the current line " case "$UNAME" in " , I am not able to resolve it and adding a tab on that line is throwing ‘recipe commences before first target. Stop.’ error
Makefile:33: *** missing separator. Stop
#! /bin/sh Makefile
# On GNU/Linux you must: alias make "/bin/sh Makefile $1" for this to work!!
#
# PURPOSE: Chooses specific Makefile
#
# NORMAL USAGE: make [version-selector]
#
# SAMPLE USAGE: make
# make [mp][f][d] e.g.
# on Linux
# make mp (to create spider_linux_mp)
# make all (to create all current executables)
# make new (to update all current executables version)
TARGET="$1"
echo TARGET: $TARGET
DEST="DEST=_tmp"
DEBUG=
if expr "$TARGET" : '.*d' > /dev/null
then
DEBUG="QFFLAGS=-g QLFLAGS=-g SUFFIX=d"
# echo DEBUG: $DEBUG
TARGET=`echo $TARGET | tr -d 'd'`
fi
UNAME=`uname`
#echo UNAME: $UNAME
UNAMEm=`uname -m`
#echo UNAMEm: $UNAMEm
case "$UNAME" in
# Find current operating system
Linux)
PLATFORM=_linux
#VERSIONS=" _linux _linux_mp_opt64 _linux_mp_intel _linux_mpi_opt64 _linux_mp_intel64 "
VERSIONS=" _linux _linux_mp_opt64 _linux_mp_intel _linux_mp_intel64 "
if expr "$TARGET" : '.*mp' > /dev/null
then # Want mp with version
WHICH="mp"
else
if expr "$TARGET" : '.*f' > /dev/null
then # Want version
WHICH=""
fi
fi;;
esac

Makefile: redefining variable in runtime using #if,eval: variable always takes value of last eval

So I'm trying to hack one of my makefiles to be simpler (simpler, as if, not defining a lot of rules how to transform subdirectory into .deb).
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
$(eval STATUS_REBUILD=1) \
echo "component: file not found: 1"; exit;\
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
$(eval STATUS_REBUILD=1) echo "component: newer files exists: 1"; exit;\
else \
$(eval STATUS_REBUILD=0) echo "component: no newer files: 0"; \
fi;\
fi
#echo "status $(STATUS_REBUILD)"
actual-target: build-if-need-status-vars
ifeq ($(STATUS_REBUILD), 1)
#echo first status: 1
else
#echo second status: 0
#echo different action
endif
all: actual-target
.PHONY: actual-target
Test with:
mkdir -p test/{sources,debs}; touch test/debs/1.deb; sleep 2; touch test/sources/1.src; (create makefile there and run)
Result:
component: file not found: 1
status 0
second status: 0
Regardless of what conditional block is executed, STATUS_REBUILD will always be 0 (last evaluated value), try it: touch test/debs/1.deb
So it seems that last $(eval) is always used.. How to avoid this behaviour and keep the correct assigned value (from first match in build-if-need-status-var)?
$(eval) is a make-level function. It is expanded in your recipe during recipe the recipe expansion stage.
The contents of a recipe are expanded in the second phase of makefile parsing (discussed briefly in the manual here).
I believe, but cannot say for sure (without testing), that recipes are not expanded until they are about to be run (but for the purposes here that doesn't change anything either way).
So your problem here is that all the $(eval) calls are expanded by the time make goes to run your shell script so you always see the last value in effect when the last line is run.
That all being said you don't actually need a make-level variable here. Your recipe is already only two shell executions.
You can simply include the last line in the same execution as the first (split) line and use a shell variable.
build-if-need-status-vars:
#if [ ! -f debs/1.deb ]; then \
STATUS_REBUILD=1; \
echo "component: file not found"; \
else \
if [ $(shell find sources/ -newer debs/1.deb 2>/dev/null | wc -l) -gt 0 ]; then \
STATUS_REBUILD=1; echo "component: newer files exists"; \
else \
STATUS_REBUILD=0; echo "component: no newer files"; \
fi;\
fi; \
echo "status $$STATUS_REBUILD"
Note that I needed to remove the exit pieces to make this work. If those are necessary in the real makefile (because this is a stripped down sample) then you can keep them by wrapping the if in a sub-shell and/or by rewriting the recipe.

How To test the exit status, and do something in Makefile

I'm trying to do this ..... and this is how my Makefile look like
.PHONY: run
SHELL := /bin/tcsh
run:
md5sum -c md; \
if ($$?==0) then \
echo "PASS" \
else \
echo "FAIL" \
endif
But i got this error.
if: Badly formed number.
make: *** [run] Error 1
Is what I'm doing correct? Or is there a better way of doing that in a Makefile?
Basically, you should simply not, ever, use csh (or tcsh) for writing makefile rules. Write the rule using POSIX shell:
.PHONY: run
run:
md5sum -c md; \
if [ $$? -eq 0 ]; then \
echo "PASS"; \
else \
echo "FAIL"; \
fi

Resources