How to read $HOME in Makefile? - makefile

I want to print the $HOME path in a Makefile:
home:
echo $HOME
I expect to print
▸ make home
/Users/leo
But I got:
▸ make home
echo $HOME
OME
How to read the $HOME path in Makefile?

Use $() in Makefiles
echo $(HOME)

You can use this as well:
echo ${HOME}
and this as well
echo $${HOME}
Because, make converts all environment variable into make variables. Please note the value of your $HOME is coming from the environment exported in the shell you are running make from.

Related

Bash: Path to symlink which calls this script

I have the following situation:
I have a script with a path of: /usr/local/bin/rsnapshot.period
I want to have symlinks to it in various /etc/cron.[period]/ directories, like /etc/cron.hourly/rsnapshot
I'd like to have the script look up the full path to the symlink, and pull out the [period] part, so I can feed it to rsnapshot.
I can do all the text hacking. The problem I'm having trouble with is getting the path to the calling symlink from within the bash script. $0 seems to point to /usr/local/bin/rsnapshot.period
Is there a better way to get this info?
$0 seems to point to /usr/local/bin/rsnapshot.period
$0 is set by the calling program in its exec*() call, as the first word of the arg argument or the first element of the argv argument. If you feel that the tool you're using is setting this value incorrectly then you should open a bug with the developer.
In the meantime, using a hardlink instead of a symlink will allow you to detect the script name properly, but will break if you aren't careful with the tool you use to edit the main script.
Turns out my problem wasn't that $0 was incorrect - it was pointing to the right place. However, as I was trying to get the absolute path of it, I was using 'realpath' before it, which resolved symlinks.
Passing realpath the '-s' fixed it. Here's my test script and the output of it:
Script:
#!/bin/sh
echo \$0: $0
echo realpath -s \$0: $(realpath -s $0)
echo readlink -e: $(readlink -e $0)
Executed:
$0: ./rsnapshot
realpath -s $0: /etc/cron.hourly/rsnapshot
readlink -e: /usr/local/bin/rsnapshot.period

Get current directory and concatenate a path

This is a shell script (.sh file). I need to create an absolute path based on the current directory. I know about pwd, but how do I concatenate it with another string? Here is an example of what I am trying to do:
"$pwd/some/path"
Sounds like you want:
path="$(pwd)/some/path"
The $( opens a subshell (and the ) closes it) where the contents are executed as a script so any outputs are put in that location in the string.
More useful often is getting the directory of the script that is running:
dot="$(cd "$(dirname "$0")"; pwd)"
path="$dot/some/path"
That's more useful because it resolves to the same path no matter where you are when you run the script:
> pwd
~
> ./my_project/my_script.sh
~/my_project/some/path
rather than:
> pwd
~
> ./my_project/my_script.sh
~/some/path
> cd my_project
> pwd
~/my_project
> ./my_script.sh
~/my_project/some/path
More complex but if you need the directory of the current script running if it has been executed through a symlink (common when installing scripts through homebrew for example) then you need to parse and follow the symlink:
if [[ "$OSTYPE" == *darwin* ]]; then
READLINK_CMD='greadlink'
else
READLINK_CMD='readlink'
fi
dot="$(cd "$(dirname "$([ -L "$0" ] && $READLINK_CMD -f "$0" || echo "$0")")"; pwd)"
More complex and more requirements for it to work (e.g. having a gnu compatible readlink installed) so I tend not to use it as much. Only when I'm certain I need it, like installing a command through homebrew.
Using the shell builtin pwd in a command substitution ($(...)) is an option, but not necessary, because all POSIX-compatible shells define the special $PWD shell variable that contains the current directory as an absolute path, as mandated by POSIX.
Thus, using $PWD is both simpler and more efficient than $(pwd):
"$PWD/some/path" # alternatively, for visual clarity: "${PWD}/some/path"
However, if you wanted to resolve symlinks in the directory path, you DO need pwd, with its -P option:
"$(pwd -P)/some/path"
Note that POSIX mandates that $PWD contain an absolute pathname with symlinks resolved.
In practice, however, NO major POSIX-like shell (bash, dash, ksh, zsh) does that - they all retain symbolic link components. Thus, the (POSIX-compliant) pwd -P is needed to resolve them.
Note that all said POSIX-like shells implement pwd as a builtin that supports -P.
Michael Allen's helpful answer points out that it's common to want to know the directory of where the running script is located.
The challenge is that the script file itself may be a symlink, so determining the true directory of origin is non-trivial, especially when portability is a must.
This answer (of mine) shows a solution.
wd=`pwd`
new_path="$wd/some/path"
with "dirname $0" you can get dynamin path upto current run scipt.
for example : your file is locateted in shell folder file name is xyz and there are anthor file abc to include in xyz file.
so put in xyz file LIke:
php "`dirname $0`"/abc.php

In shell script, how to change current directory safely with variable?

The following shell script changes current the directory to the desktop.
v=~/Desktop/
cd $v
pwd # desktop
The following script changes the current directory to home directory instead of generating error.
cd $undefined_variable
pwd # home directory
echo $? # 0
I'm afraid that the script will remove important files if I misspelled a variable for new current directory.
Generally, how do you safely change current directory with variable in shell script?
Use:
cd ${variable:?}
if $variable is not defined or empty then bash will throw an error and exit. It's like the set -u option but not global through the file.
You can set -u to make bash exit with an error each time you expand an undefined variable.
You could use the test -d condition (checks whether the specified variable is a directory), i.e.
if [[ -d $undefined_variable ]]
then
cd $undefined_variable
echo "This will not be printed if $undefined_variable is not defined"
fi
See also here for further test options...
The Bourne Shells have a construct to substitute a value for undefined variables, ${varname-subtitution}. You can use this to have a safe fallback directory in case the variable is undefined:
cd "${undefined-/tmp/backupdir}"
If there is a variable named undefined, its value is substituted, otherwise /tmp/backupdir is substituted.
Note that I also put the variable expansion in double quotes. This is used to prevent word splitting on strings containing spaces (very common for Windows directories). This way it works even for directories with spaces.
For the gory details on all the shell substitution constructs (there are seven more for POSIX shells), read your shell manual's Parameter Substitution section.
You have to write a wrapper (this work in bash):
cd() {
if [ $# -ne 1 ] ;then
echo "cd need exactly 1 argument" >&2
return 2
fi
builtin cd "$1"
}
yes, that's shell
if you type cd without parameter it will jump to home dir.
You can can check the variable of null or empty before you cd command.
check like (cd only be called if targetDir is not empty):
test -z "$targetDir" || cd $targetDir
check like (cd only be called if targetDir really exist):
test -d "$targetDir" && cd $targetDir
Note: Thanks for -1, should read the last sentence too. So I added the real answer.

How to get parent folder of executing script in zsh?

In bash i get the executing script's parent folder name by this line
SCRIPT_PARENT=`readlink -f ${BASH_SOURCE%/*}/..`
Is there any way to achieve this in zsh in a way that works both in zsh and bash?
Assume i have got a file /some/folder/rootfolder/subfolder/script with the contents:
echo `magic-i-am-looking-for`
I want it to behave this way:
$ cd /some/other/folder
$ . /some/folder/rootfolder/subfolder/script
/some/folder/rootfolder
$ . ../../folder/rootfolder/subfolder/script
/some/folder/rootfolder
$ cd /some/folder/rootfolder
$ . subfolder/script
/some/folder/rootfolder
$ cd subfolder
$ . script
/some/folder/rootfolder
This should work in bash and zsh. My first implements this behavior, but does due to $BASH_SOURCE not work in zsh.
So basically its:
Is there a way to emulate $BASH_SOURCE in zsh, that also works in bash?
I now realized that $0 in zsh behaves like $BASH_SOURCE in bash. So using $BASH_SOURCE when available and falling back to $0 solves my problem:
${BASH_SOURCE:-$0}
There is a little zsh edge case left, when sourcing from $PATH like:
zsh> cat ../script
echo \$0: $0
echo \$BASH_SOURCE: $BASH_SOURCE
echo '${BASH_SOURCE:-$0}:' ${BASH_SOURCE:-$0}
zsh> . script
$0: script
$BASH_SOURCE:
${BASH_SOURCE:-$0}: script
bash> . script
$0: bash
$BASH_SOURCE: /home/me/script
${BASH_SOURCE:-$0}: /home/me/script
I could do a which script but this would not play nice with other cases
While it would be easy to do this in zsh, it is just as easy to use pure bash which is able to be evaluated in zsh. If you cannot use any command that may or may not be on your path, then you can only use variable alteration to achieve what you want:
SCRIPT_SOURCE=${0%/*}
This is likely to be a relative path. If you really want the full path then you will have to resort to an external command (you could implement it yourself, but it would be a lot of work to avoid using a very available command):
SCRIPT_SOURCE=$(/bin/readlink -f ${0%/*})
This doesn't depend on your $PATH, it just depends on /bin/readlink being present. Which it almost certainly is.
Now, you wanted this to be a sourced file. This is fine, as you can just export any variable you set, however if you execute the above then $0 will be the location of the sourced file and not the location of the calling script.
This just means you need to set a variable to hold the $0 value which the sourced script knows about. For example:
The script you will source:
echo ${LOCATION%/*}
The script that sources that script:
LOCATION=$0
<source script here>
But given that the ${0%/*} expansion is so compact, you could just use that in place of the script.
Because you were able to run the command from your $PATH I'll do something like that:
SCRIPT_PARENT=$(readlink -f "$(which $0)/..")
Is that your desired output?

Command-line variable assignments in Cygwin Bash not working?

According to section 3.7.1 of the Bash manual, variable assignments at the beginning of a command line should be visible to the invoked program.
e.g.
DIR=/tmp ls $DIR
should behave as if I've typed "ls /tmp" - and the variable DIR should not persist after executing the command.
Cygwin Bash (GNU bash, version 3.2.51(24)-release (i686-pc-cygwin)) appears not to do this - the above command behaves as if $DIR is not defined. This is confirmed by other tests such as "DIR=/tmp echo $DIR", "DIR=/tmp set" etc.
Note that adding a semicolon works ("DIR=/tmp ; ls $DIR"), but leaves the variable defined after the command.
Why is this not working as expected?
It does work - but not in the context that you are trying to make it work.
DIR=/tmp ls $DIR
The environment variable DIR is set for ls - but is not set when the shell expands the $DIR of the command. This is the way the Bourne shell behaved; it is the way its successors such as the Korn shell and Bash behave.
You could see that DIR is set by changing ls $DIR to env; that would show the environment of an external (not built-in) command.
In this example, think about it for a moment: what you've typed is 9 extra characters compared with:
ls /tmp
If you must have it set and removed, then this does the trick:
(DIR=/tmp; ls $DIR)
The variable is set before the shell evaluates ls $DIR, but the whole command is run in a sub-shell so it has no impact on the invoking shell.

Resources