I googled for this, but I can't figure out why Bash complains with the following code to check if a directory exists:
test.mk
#!/bin/bash
MYDIR="dl"
all:
if [ ! -d $MYDIR ]; then
#if [ ! -d "${MYDIR}" ]; then
#if [ ! -d ${MYDIR} ]; then
#Here
fi
make -f test.mk
if [ ! -d YDIR ]; then
/bin/sh: Syntax error: end of file unexpected
make: *** [all] Error 2
Does someone know why it fails? And why does it call /bin/sh instead of /bin/bash? Thank you.
Edit: unlike Bash, make doesn't support multi-line block. Here's working code:
MYDIR="dl"
all:
if [ ! -d ${MYDIR} ]; then\
echo "Here";\
else\
echo "There";\
fi
The #!/bin/bash shebang that you inserted at top is useless, and it is treated by make as a comment.
make sends by default commands to /bin/sh. To specify a different shell, use the macro SHELL = /bin/bash.
Moreover, you need to escape your variable:
if [ ! -d ${MYDIR} ]
I'm not sure if make can handle multi-line statements, so try to put all the if block in a line.
if [ ! -d ${MYDIR} ]; then DO_SOMETHING; DO_SOMETHING_ELSE; fi
You're feeding test.mk to make, not to bash. Then make sends individual lines to the shell, not whole blocks.
make uses its SHELL macro to determine which shell to use. You can override it to make it use bash.
The reason why you're getting YDIR is that make has silly rules about variable interpolation. Write $(MYDIR), not $MYDIR.
try bracing your variable:
${MYDIR}
Related
I have this makefile:
SHELL := /bin/bash -f
working :
if [ -d ffprob_runfail ]; then echo "gotcha" ;fi
error :
if [ -d ffprob_* ]; then echo "gotcha" ;fi
Executing 'make working' in a folder where the subdirectory 'ffprob_runfail' exists echoes:
if [ -d ffprob_runfail ]; then echo "gotcha" ;fi
gotcha
Executing 'make error' in the same folder echoes:
if [ -d ffprob_* ]; then echo "gotcha" ;fi
I am not sure where this 'surprising' behavior comes from - either miscoding in make or bash syntax. I tried escaping * but did not work. Might be an issue with the syntax of [ ] bash operator? (I am quite new to bash, after 20 years of csh pain...)
Any hint appreciated.
POST EDIT:
Not only the -f option disables globbing (thanks #choroba), but also the -d operator in bash is unary, and cannot used safely with globbing, i.e. refer to Bash Shell Script: confirm the existance of one or more directories.
So this looks to be the right way (continuation of the previous makefile...):
right :
for item in ffprob_* ; do if [ -d "$$item" ] ; \
then echo "gotcha $$item";fi;done
The -f option for bash means the same as the -f option to set, namely
-f Disable file name generation (globbing).
With globbing disabled, wildcards aren't expanded.
So, why do you set the shell to bash -f? Remove the -f.
I'm trying to check if a folder exists. If it doesn't, I create it.
I have this code:
if [ $(is_dir "$contaniningdir/run") = "NO"]; then
mkdir "$containingdir/run"
fi
However, I'm getting:
is_dir: command not found
So how what's the correct way of doing this?
You should use
if [ ! -d "$DIRECTORY" ]; then
# your mkdir and other stuff ...
fi
as per this question/answer.. Another relevant question/answer is here.
One of the comments also mentions an important notice:
One thing to keep in mind: [ ! -d "$DIRECTORY" ] will be true either
if $DIRECTORY doesn't exist, or if does exist but isn't a directory.
For more you should probably check that other question's page.
is_dir is a PHP function that you probably mixed with bash unintentionally :)
bash is capable of checking for the existence of a directory without external commands:
if [ ! -d "${containingdir}/run" ]; then
mkdir "${containingdir}/run"
fi
! is negation, -d checks if the argument exists and is a directory
I currently have this bash script (which is located in my home directory, i.e., /home/username/ and I am running it as root as it's necessary for the icon copying lines):
cd /home/username/Pictures/Icon*
declare -a A={Arch,Debian,Fedora,Mageia,Manjaro,OpenSUSE}
declare -a B={Adwaita,Faenza,gnome,Humanity}
for i in $A; do
for j in $B; do
if test -e /usr/share/icons/$j/scalable ; else
mkdir /usr/share/icons/$j/scalable/
fi
if test -e /usr/share/icons/$j/scalable/$i.svg ; else
cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg
fi
done
done
What I want this script to do is to copy icons from my Pictures/Icons and logos directory to the scalable theme (specified in $B) subdirectories in /usr/share/icons. Before it does this, however, I'd like it to create a scalable directory in these theme subdirectories if it does not already exist. The problem is that the else part of the conditionals is not being read properly, as I keep receiving this error:
./copyicon.sh: line 8: syntax error near unexpected token `else'
./copyicon.sh: line 8: ` if test -e /usr/share/icons/$j/scalable ; else'
If you're wondering why the test -e ... in the conditional it's based on a textbook on bash scripting I've been following.
Checking file and/or directory existence
To check whether a file exists in bash, you use the -f operator. For directories, use -d. Example usage:
$ mkdir dir
$ [ -d dir ] && echo exists!
exists!
$ rmdir dir
$ [ -d dir ] && echo exists!
$ touch file
$ [ -f file ] || echo "doesn't exist..."
$ rm file
$ [ -f file ] || echo "doesn't exist..."
doesn't exist...
For more information simply execute man test.
A note on -e, this test operator checks whether a file exists. While this may seem like a good choice, it's better to use -f which will return false if the file isn't a regular file. /dev/null for example is a file but nor a regular file. Having the check return true is undesired in this case.
A note on variables
Be sure to quote variables too, once you have a space or any other special character contained in a variable it can have undesired side effects. So when you test for existence of files and directories, wrap the file/dir in double quotes. Something like [ -f "/path/to/some/${dir}/" ] will work while the following would fail if there is a space in dir: [ -f /path/to/some/${dir}/ ].
Fixing the syntax error
You are experiencing a syntax error in the control statements. A bash if clause is structured as following:
if ...; then
...
fi
Or optional with an else clause:
if ...; then
...
else
...
fi
You cannot omit the then clause. If you wish to only use the else clause you should negate the condition. Resulting in following code:
if [ ! -f "/usr/share/icons/$j/scalable" ]; then
mkdir "/usr/share/icons/$j/scalable/"
fi
Here we add an exclamation point (!) to flip the expression's evaluation. If the expression evaluates to true, the same expression preceded by ! will return false and the other way around.
You can't skip the then part of the if statement, easiest solution would be to just negate the test
if [[ ! -e /usr/share/icons/${j}/scalable ]] ; then
mkdir /usr/share/icons/${j}/scalable/
fi
if [[ ! -e /usr/share/icons/${j}/scalable/${i}.svg ]] ; then
cp -a ${i}*.svg /usr/share/icons/${j}/scalable/${i}.svg
fi
I left it with -e (exists), but you might consider using -d for directories or -f for files and some error handling to catch stuff (e.g. /usr/share/icons/$j/scalable/ exists, but is a file and not a directory for whatever reason.)
I also noticed that in your original code you are potentially trying to copy multiple files into one:
cp -a $i*.svg /usr/share/icons/$j/scalable/$i.svg
I left it that way in my example in case you are sure that it is always only one file and are intentionally renaming it. If not I'd suggest only specifying a target directory.
What does the #[arguments] syntax in a makefile mean? For example, I have come across a makefile that contains the following:
all: $(EXEC_NAME)
#[ -p video_fifo ] || mkfifo video_fifo
What does this do?
Nothing. You are parsing it incorrectly.
The leading # is for make. It means "silence this command". See Recipe Echoing in the GNU make manual.
The rest of the line is a shell script. In this case it starts with a test [ -p video_fifo ] (you often see this in the context of an if statement if [ -n "$var" ] or whatever see the bash manual for more about [/test and for the -p flag) and then || mkfifo video_fifo to create the video_fifo fifo if the [ test returns false (hence the || OR operator).
I found that I can use ifneq in makefile and I tried to compare 0 and the output of command stat:
#for f in `find $(PATH_PAGES) -name *.hbs`; do \
ifneq "`stat -c '%Y' $$f`" "0";
//some code here
endif
done
But in terminal I've got an error: ifneq: command not found
Is there a different way to compare this or maybe I'm doing something wrong?
In this case you don't want to use Make's ifneq, because it does text substitution before handing over the command to the shell, but you have a shell loop that needs to do different things in each iteration depending on the output of a shell command.
Use the shell if instead:
if [ "`stat -c '%Y' $$f`" != "0" ]; then
//some code here
fi
If you want to use makefile's if condition then there should not be [TAB] before the if statement because if you specify [TAB] then it is treated as shell command thats why you are getting error that ifneq:command not found its not there in shell.
May be this Conditionals in Makefile: missing separator error?
can help in getting better understanding with makefiles
I found that I needed to prepend the if with a #, and backslashes proved to be necessary as well -
#if [ "`stat -c '%Y' $$f`" != "0" ]; then\
echo hello world;\
fi