OS X bash: dirname - bash

I want to create a simple bash script to launch a Java program on OS X. The names of the file, the file path, and the immediate working folder all contain spaces. When I do this:
#!/bin/sh
cd `dirname $0`
I get
usage: dirname path
I have also tried putting quotes in all kinds of different places. The most elaborate example being
cd "`dirname \"$0\"`"
Nothing has worked. I either get error messages or that cryptic "usage: dirname path" message.
What are other methods that might work?
Edit: this doesn't seem to be an issue for anyone but me so it must just be my box. I'm going to accept my own post below because it's the only solution which worked for this specific problem. However I'm definitely upvoting the solutions which seem to be working for everyone else and really appreciate everyone's help.

What about:
cd "$(dirname "$0")"
That works for me here.

#!/bin/sh
cd "$(dirname "$0")"

What finally worked for me is changing this:
#!/bin/sh
cd `dirname $0`
To this:
#! /bin/zsh
cd "${0:h}"
This also supports file names and file paths containing spaces. Here's where I found it: http://rentzsch.com/unix/locationAwareCommandFiles

Escaping the inner double quotes is unnecessary:
cd "`dirname "$0"`"
But that doesn't get to the root of the problem, which is that somehow the value of $0 appears to be empty, or perhaps something strange. Try changing running your script this way:
bash -x scriptname
This will echo each line, with variables interpolated, before running it. It is very useful for debugging. Also quite helpful are:
-u: abort on attempt to use undefined variable
-e: abort on first error

Hey not sure about this... But is it possible that your
#!/bin/sh
Points to something that is not bash? What I usually use is:
#!/usr/bin/bash
Pretty new to the whole scripting thing so not sure.

Related

Changing PWD in a script to allow for accessing file without prefixing full path

I know I should be able to change the current working directory of a bash script by doing something akin to
cd `dirname $MYPATH`
but for some reason this doesn't work (or not as I imagined it).
#!/bin/bash
WAYPATH="/home/user/articles"
TEST_PATH="/home/user/testing"
# Set working directory of the script to be testing
cd `dirname $TEST_PATH`
for i in $(ls $WAYPATH); do
another_command $i $i.r > $TEST_PATH/htmls/$i.html
done
My goal here is to allow the bash script to find the files located in TEST_PATH (which have matching name to those in WAY_PATH) without having to prefix them with the full path (because another_command) makes use of the whole argument passed to it.
So this is a lesson on understanding what commands do after reading about them on Stackexchange. I was using
cd `dirname $MYPATH`
following this answer where they achieved the desired result
cd `dirname $0`
$0 is the full path of the bash script, so dirname is required to return the path without the name of the file.
Instead, for an arbitrary supplied path is sufficient to do a simple
cd $MYPATH
as suggested in comments.

Parent directory of a script

So I am a rookie in Linux and I need some help. I have to write a bash script in which I have to use the parent directory of the script to create a file there, wherever the script would be. It should look like this:
If my script it's in "/home/student/", I need to create, using an in-script command another file called txt in /home/. Any ideas please? Thank you.
There's a subtlety if you want to be able to run your script from anywhere.
eg: if your script is in /home/myHome/someDir/someOther, and you want to create a file in /home/myHome/someDir wherever you are when you run your script.
To solve it, you just need to first derive the directory where your script is.
It can be done using:
SCRIPT_DIRECTORY="$(dirname "$0")"
touch "$SCRIPT_DIRECTORY/../myFile.txt"
Edit: Actually it can be even more subtle, if you want to handle symlinks. ie: if the symlink /home/myHome/mySymlink points at your script, and is the one actually being called, then the previous script will consider /home/myHome/ instead of /home/myHome/someDir/someOther
To handle this case you can do
if [ -L "$0" ] && [ -x $(which readlink) ]; then
ACTUAL_SCRIPT_FILE="$(readlink -mn "$0")"
else
ACTUAL_SCRIPT_FILE="$0"
fi
SCRIPT_DIRECTORY="$(dirname "$ACTUAL_SCRIPT_FILE")"
touch "$SCRIPT_DIRECTORY/../myFile.txt"
use .. to point to parent directory. So you could create a file using something like
MY_SCRIPTDIR="$(dirname $0)"
touch ${MY_SCRIPTDIR}/../abc.txt
From your command prompt or within shell script.
Unfortunately, the other answers either give you the current working directory instead of the directory the script is in, or they will not work if either the script or one of the directories along the way is a symbolic link rather than a real directory.
What will work is:
dirname $(readlink -f "$0")
Explanation:
"$0" is the name of the script as you type it in your command line. Quoting is important for the case it contains whitespace.
readlink will resolve any symbolic links along the way
dirname takes just the directory name from script's full path - it's better readable and safer for corner cases than manually looking for slashes etc.
Now, you will get the correct result even in a complex case: if your script is in /tmp and you create a symbolic link to it in /tmp/abc/, and your current directory will be /home and you run /tmp/abc/your-script, it will correctly output /tmp, not /home nor /tmp/abc.

Bash script: referencing a folder path from the script folder, and not from where I run it

I just bumped into a bug in redis install_server script
it has a hardcoded :
DEFAULT_CONFIG="../redis.conf"
so when running this script not from its own folder (such as ./utils/install_server.sh)
the script fails to find the conf file.
I'm looking for a way to reference the scripts folder without a dependency on where the script is called from.
I looked into this answer which seems to be the canonical on SO but something is failing for me:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo $DIR
and I get:
./utils/install_server.sh: 100: ./utils/install_server.sh: Bad substitution
/home/myusername/binaries/redis-2.8.3 #where I run the script from..not the folder I need
So I guess I'm doing something wrong, or this isn't the correct answer for me
I know I can add a check if the file exists and a clearer error message to the redis install script, but I rather just make this work.
I'll be glad for ideas, and I'll make a PR to redis to fix this for everyone..
Thanks!
I do something very similar to what you have posted:
SCRIPT_DIR="$(cd $(dirname $0) && pwd)"
It seems that in your case, something is wrong with BASH_SOURCE. In my approach, I use $0 which always evaluates to the full pathname used to launch the script.
I'm not sure what the problem with BASH_SOURCE is in your script as what you posted works for me. Thus I am just offering an alternate approach.
Are you running this with bash (i.e. start the script with #!/bin/bash or run it with bash /path/to/script), or plain sh (#!/bin/sh or sh /path/to/script)? Both BASH_SOURCE and arrays ([0]) are bash extensions, and may not be available in a generic shell. In particular, the "Bad substitution" error is one I've seen from trying to use arrays in a shell that doesn't support them.

cp in a bash script

I am working on a simple shell script (my first one) and cannot seem to figure out why this is failing when I run ./auto.sh baz. It was working initially when I had hard coded in the full destination path, but now that I want to use an argument it breaks. Any help would be awesome. Thanks!
#!/bin/sh
cp -ivr ./foo/bar.xcodeproj ./iOS/$1.xcodeproj
Try the following
#!/bin/sh
cp -ivr "./foo/bar.xcodeproj" "./iOS/${1}.xcodeproj"

Find file's own path

I'm trying to find what the unix equivalent of the Windows/DOS variable %cd% is. What I'm looking for is an environmental variable or a workaround that will let me set a variable to the path of the file currently running.
For example, if the program is in /home/chris/Desktop but the working directory is /home/chris, what would be the command to get ~/Desktop as opposed to pwd which will give me /home/chris.
In BASH, you can look at the $PWDvariable. That'll show your Present Working Directory. Getting the relationship between the $PWD and where the program is located is a bit trickier. You can look at the $0 variable which should give you the name of the file I ran the following script:
#! /bin/bash
#
echo "PWD = $PWD"
echo "\$0 = $0"
And got the following result:
$ test.sh
PWD = /Users/david
$0 = /Users/david/bin/test.sh
The $0 gives you the name of the file from the root of the OS. Taking the dirname will give you the file name. Somehow, if you can filter out the PWD from the $0, you might get what you're looking for. I had some luck with the following:
curPath=$(dirname "${0#$PWD/}")
Didn't thoroughly test it, from what I can see, it seems to do what you want. What it can't do is do something like this:
$ test.sh
PWD = /Users/david/someSubDir
$0 = /Users/david/bin/test.sh
The current path is /Users/david/bin/test.sh
It would have been nice if it could do this:
The current path is ../bin/test.sh
Although the former is correct.
The readlink command doesn't work on non-Linux systems.
How about dirname $(readlink -f $0)
readlink -f $0 returns the canonicalized path to the running script.
dirname removes everything after and including the final \.
This way works, but isn't 100% reliable:
${0%/*}
The way that works is that it reads $0 (the program name), and strips off everything from the final slash onwards. It's not reliable because if your script is invoked via a symlink, you will get the directory containing the symlink, not the directory containing the real script.
Also, it's possible to pass in a "fake" value for $0, for example by using exec -a. So even if you aren't using symlinks, it's still not a 100% solution. In fact, such a solution doesn't exist.
Working with what Chris suggested, you could use the which command. According to the man page, which reports the full path of the executable that would have been executed if its argument had been entered at the shell prompt. Since we know $0 was entered at the shell prompt, we can use `which $0` to report exactly the path that was used to execute. Unfortunately, this still suffers from the symlink issue, as which does not provide options to avoid symlinks.

Resources