Running Linux system commands from Ruby script - ruby

I have the following Ruby script that creates a Debian package, which works fine:
#!/usr/bin/ruby
dest = "#{File.dirname(__FILE__)}/../build"
package = "foo"
[
"cd #{dest} && tar czvf data.tar.gz bin console data.sql etc filter install.rb",
"cd #{dest} && tar czvf control.tar.gz control",
"cd #{dest} && echo 2.0 > debian-binary",
"cd #{dest} && ar -cr #{package}.deb debian-binary control.tar.gz data.tar.gz",
"cd #{dest} && mv #{package}.deb ..",
"cd #{dest} && rm data.tar.gz control.tar.gz",
].each do |command|
puts command
system(command)
end
Is there a way in Ruby where I can leave off the "cd #{dest} &&" part of each command?

Dir.chdir(dest) do
# code that shall be executed while in the dest directory
end
Dir.chdir when invoked with a block will change to the given directory, execute the block and then change back.
You can also use it without a block, in which case it will never change back.

Yes. Use Dir.chdir:
#!/usr/bin/ruby
dest = "#{File.dirname(__FILE__)}/../build"
package = "foo"
Dir.chdir dest
[
"tar czvf data.tar.gz bin console data.sql etc filter install.rb",
"tar czvf control.tar.gz control",
"echo 2.0 > debian-binary",
"ar -cr #{package}.deb debian-binary control.tar.gz data.tar.gz",
"mv #{package}.deb ..",
"rm data.tar.gz control.tar.gz",
].each do |command|
puts command
system(command)
end

Related

BASH script file - run command with alias in interactive mode -> prevent logout/exit [duplicate]

I know that it works for alias instructions in the file .bashrc.
However, a shell script ln_hook:
#!/bin/sh
#filename: ln_hook
## http://stackoverflow.com/questions/3648819/how-to-make-symbolic-link-with-cygwin-in-windows-7
## https://cygwin.com/cygwin-ug-net/using-cygwinenv.html
## https://cygwin.com/cygwin-ug-net/using.html#pathnames-symlinks
# export CYGWIN="winsymlinks" # ln -s: The shortcut style symlinks with file extension '.lnk'
# export CYGWIN="winsymlinks:native" # ln -s: plain text file
ln_hook(){
if [[ "-s" == "$1" ]]; then
cmd /C mklink /H "$(cygpath -aw "$3")" "`cygpath -aw "$2"`"
else
echo -e "\033[32m >> $* \033[0m"
ln "$*"
fi
}
alias ln='ln_hook'
type ln
(type ln)
# cannot propagate to another shell script
./test_ln
$(dirname $0)/test_ln
## . ./ln_hook
## type ln
and, another shell script:
#!/bin/sh
#filename: test_ln
type ln
Then run . ./ln_hook, output:
ln_hook
ln 是 `ln_hook' 的别名
ln 是 `ln_hook' 的别名
ln 是 /usr/bin/ln
ln 是 /usr/bin/ln
Is there a workaround to make the alias effective when running other scripts?
After going into Bash Manual and google, export -f function_name is what I want.
Thank Etan Reisner for the advice about function being recursive and a demo code as follow:
#!/bin/bash --posix
# Avoid an error about function xx() statement on some os, e.g. on ubuntu, Syntax error: "(" unexpected
# http://ubuntuforums.org/archive/index.php/t-499045.html
#########################################################################################################
# << A example about exporting a function >>
# hook ln and propagate it to other scripts to pollute the environment of subsequently executed commands
#########################################################################################################
## https://stackoverflow.com/questions/3648819/how-to-make-symbolic-link-with-cygwin-in-windows-7
## https://cygwin.com/cygwin-ug-net/using-cygwinenv.html
## https://cygwin.com/cygwin-ug-net/using.html#pathnames-symlinks
# export CYGWIN="winsymlinks" # ln -s: The shortcut style symlinks with file extension '.lnk'
# export CYGWIN="winsymlinks:native" # ln -s: plain text file
## ln_hook
function ln(){
if [[ "-s" == "$1" ]]; then
cmd /C mklink /H "$(cygpath -aw "$3")" "`cygpath -aw "$2"`"
else
echo -e "\033[32m >>ln $* \033[0m"
command ln "$*"
fi
}
## Cannot propagate alias to other scripts
## alias ln='ln_hook'
## export -f ln=ln_hook ## ln_hook.sh: 第 23 行:export: ln=ln_hook: 不是函数
## http://docstore.mik.ua/orelly/unix3/upt/ch29_13.htm
## https://stackoverflow.com/questions/1885871/exporting-a-function-in-shell
export -f ln
echo&&echo "[^-^] After trying ln_hook"
echo -n "main shell: " && type ln
echo -n "subshell: " && (type ln)
echo&&echo "[^-^] Run an external script"
echo 'type ln' > test_ln.sh
./test_ln.sh
# $(dirname $0)/test_ln.sh
## . ./ln_hook
echo 'You can try: `type ln`, which will output: ln 是函数...'
Here, Demo or Bash shell editor and execute online

Bash trap does not run when the exit 1 codes put at some random location

I am using the bash trap to make sure one function runs at any cost. I know trap is not specific to exitO or 1. Here is what I have done.
#!/bin/bash
set -e
#array to store server and deployed status
declare -A server_deployed
#path to file containing the server inventory
readonly filepath="/var/jenkins_home/workspace/server_list.txt"
#array to store to list of IPS
declare -A result #associative array
if [[ $TARGET == "ALL" ]]; then
while read line ; do
server_name=` echo $line | cut -d= -f1 `
result+=(["$server_name"]=${line#*=})
done < $filepath
else
singleserver=`cat $filepath | grep "$TARGET"`
server_name=`echo $singleserver| cut -d= -f1 ` # get the servername
serverip=`echo $singleserver| cut -d= -f2 ` # get tje server ip
result+=(["$server_name"]=$serverip) # gets the ip which is after equalto
fi
#function to send slack notification everytime
function sendMessage(){
for sd in "${!server_deployed[#]}"
do
echo "#########################################################"
echo "Sucecssfully deployed on $sd"
echo "#########################################################"
done
#let find the unsuccess list ,for which we need to find the array diff
for server_name in "${!result[#]}"
do
for sd in "${!server_deployed[#]}"
do
if [[ "$server_name" != "$sd" ]]; then
echo "Failed to deploy on $server_name"
fi
done
done
}
trap "sendMessage" INT EXIT
for server in "${!result[#]}"
do
upstream="MBM-TEST-ADMIN-BUILD"
#bundle filename
bundle="mbm_admin_dist"
name=$server
instance=${result[$name]}
#------copying the hash.php in build job to the deployed server home location
# ssh and move to desired location
sudo scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null /tmp/test/admin_hash.json ubuntu#$instance:/home/ubuntu/
mbmcms_workspace="/var/jenkins_home/workspace/$upstream"
cd $mbmcms_workspace
#remove if exist
if [ -e "$bundle.zip" ]
then
echo "Already Exist so Removing it First !!"
rm -f $bundle.zip
fi
#check if the dist folder exist that comes from successful build
if [ ! -d "dist" ]; then
echo "--------------------------------------------------------------"
echo "The dist folder does not exist, Please run BUILD job First !!"
echo "--------------------------------------------------------------"
exit 1
fi
#zip the file from the TEST & Build jobs
echo "starting to zip mbm-admin dist file created after test and build success!!"
zip --symlinks -x *.git* -r $bundle ./dist
#---------------------------------------------------------------
# copy development.php if non-production else copy production.php
# the file goes to the view/cms/
#---------------------------------------------------------------
sudo scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null development.php ubuntu#$instance:/home/ubuntu
sudo scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null production.php ubuntu#$instance:/home/ubuntu
#copy the bundle file to instance
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -r $bundle.zip ubuntu#$instance:/home/ubuntu/
ssh -tt -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ubuntu#$instance '
SOURCE_FOLDER='/home/ubuntu/mbm-admin/dist/'
DESTINATION_FOLDER='/home/ubuntu/wpdata/plugins/manager/app/views/admin/admin_console/dist/'
rm -rf mbm-admin
mkdir -p mbm-admin
mv mbm_admin_dist.zip mbm-admin
cd mbm-admin
unzip mbm_admin_dist.zip
rm -f mbm_admin_dist.zip
sudo rsync -arz --force --delete --progress $SOURCE_FOLDER $DESTINATION_FOLDER
#moving the copied file to the /app/views/admin/admin_console
sudo mv /home/ubuntu/development.php /home/ubuntu/wpdata/plugins/manager/app/views/admin/admin_console
sudo mv /home/ubuntu/production.php /home/ubuntu/wpdata/plugins/manager/app/views/admin/admin_console
#moving the hash.php to the desired location
sudo cp -fv /home/ubuntu/admin_hash.json /home/ubuntu/wpdata/plugins/manager/app/views/admin/admin_console/hash.json
'
server_deployed+=(["$server"]="SUCCESS")
done
Here is the trap , as seen the script section
trap "sendMessage" INT EXIT
I need to run the sendMessage function whenever some exit code is
detected or the program exits due to any error.
Problem:
When I put the exit 1 at the end of the script the function gets called by the trap but suppose, If I put it somewhere in the middle or exactly after the main for loop starts the trap does not catch the exit code.
What am I not understanding or missing exactly in here?

How to import bash aliases into fish

I am missing my bash aliases in fish, and don't want to manually convert all of them to fish functions.
How to get them all from bash to fish?
Bonus points if:
the solution supports an iterative process, as in: i can easily change the aliases in bash, and re-convert/re-import them into fish
the solution also imports bash functions
Convert bash aliases to bash scripts
I decided to do this instead of approach below, and putting the scripts into ~/bin/, which is in my PATH. This allows me to use them from whatever shell i am currently executing, and it prevents potential problems with quoting.
use like so:
# converts all bash aliases to script files
convert_bash_aliases_to_scripts
# removes all scripts previously converted by this script
convert_bash_aliases_to_scripts clean
bash conversion script:
#!/bin/bash
# Convert bash aliases to bash scripts.
#
# Copyright 2018 <hoijui.quaero#gmail.com>, licensed under the GPL-3.0+
#
# Usage:
# convert_bash_aliases_to_scripts # converts all bash aliases to script files
# convert_bash_aliases_to_scripts clean # removes all scripts previously converted by this script
COLOR_RED=$'\e[0;31m'
COLOR_ORANGE=$'\e[0;33m'
COLOR_BLUE=$'\e[0;34m'
COLOR_BLUE_LIGHT=$'\e[1;34m'
COLOR_GREEN=$'\e[0;32m'
COLOR_BROWN=$'\e[0;33m'
COLOR_YELLOW=$'\e[1;33m'
COLOR_WHITE=$'\e[1;37m'
COLOR_CYAN=$'\e[0;36m'
COLOR_PURPLE=$'\e[0;35m'
COLOR_GRAY=$'\e[1;30m'
COLOR_GRAY_LIGHT=$'\e[0;37m'
COLOR_NONE=$'\e[m' # No Color
OUTPUT_DIR=~/bin/converted/aliases
LINKS_DIR=~/bin
README_FILE_NAME="README.md"
README_FILE="$OUTPUT_DIR/$README_FILE_NAME"
if [ "$1" = "clean" ]
then
for script_file in $(find "$LINKS_DIR" -maxdepth 1 -type l)
do
conv_script_file="$OUTPUT_DIR/$(basename $script_file)"
if [ -e $conv_script_file ] && [ "$(readlink --canonicalize $script_file)" = "$(realpath $conv_script_file)" ]
then
script_name=$(basename $script_file)
echo "removing converted bash alias-script: $script_name"
rm $conv_script_file \
&& rm $script_file
fi
done
rm $README_FILE 2> /dev/null
rmdir $OUTPUT_DIR 2> /dev/null
exit 0
fi
SOURCE_FILES="${HOME}/.bashrc ${HOME}/.bash_aliases"
mkdir -p $OUTPUT_DIR
echo -e "# Bash alias conversion scripts\n\nsee $0\n\nWARNING: Do NOT manually edit files in this directory. instead, copy them to $LINKS_DIR (replacing the symbolic link that already exists there), and edit that new file.\nIf you edit the files in this dir, it will be replaced on the next (re)conversion from aliases." \
> $README_FILE
AUTO_IMPORT_WARNING="# WARNING Do NOT edit this file by hand, as it was auto-generated from a bash alias, and may be overwritten in the future. please read ${README_FILE}"
function _is_link_to {
local file_link=$1
local file_target=$2
test -e $file_target \
&& test "$(readlink --canonicalize $file_link)" = "$(realpath $file_target)"
return $?
}
for source_file in $SOURCE_FILES
do
IFS=$'\n'
for a in $(cat $source_file | grep "^alias")
do
a_name="$(echo "$a" | sed -e 's/alias \([^=]*\)=.*/\1/')"
a_command="$(echo "$a" | sed -e 's/alias \([^=]*\)=//' -e 's/[ \t]*#.*$//')"
if echo "${a_command:0:1}" | grep -q -e "[\'\"]"
then
# unquote
a_command_start=1
let a_command_end="${#a_command} - 2"
else
# leave as is
a_command_start=0
let a_command_end="${#a_command}"
fi
script_file="$LINKS_DIR/$a_name"
conv_script_file="$OUTPUT_DIR/$a_name"
# Check whether the script already exists.
# If so, we skip importing it, unless it is just a link to a previously imported script.
log_action="none"
log_action_color="${COLOR_NONE}"
log_content=""
if [ -e $script_file ] && ! $(_is_link_to $script_file $conv_script_file)
then
log_action="skipped (exists)"
log_action_color="${COLOR_ORANGE}"
log_content=""
else
if [ -e $script_file ]
then
log_action="reimporting"
log_action_color="${COLOR_BLUE}"
else
log_action="importing"
log_action_color="${COLOR_GREN}"
fi
# write the script file to a temporary location
conv_script_file_tmp="${conv_script_file}_BAK"
echo "#!/bin/bash" > $conv_script_file_tmp
echo -e "$AUTO_IMPORT_WARNING" >> $conv_script_file_tmp
echo -e "#\n# Imported bash alias '$a_name' from file '$source_file'" >> $conv_script_file_tmp
cat >> "${conv_script_file_tmp}" <<EOF
${a_command:${a_command_start}:${a_command_end}} \${#}
EOF
if diff -N ${conv_script_file_tmp} ${conv_script_file} > /dev/null
then
log_content="no change"
log_content_color="${COLOR_NONE}"
else
log_content="changed"
log_content_color="${COLOR_GREEN}"
fi
log_content=$(printf "%s %10s -> %s${COLOR_NONE}" "${log_content_color}" "${log_content}" "$a_command")
mv "${conv_script_file_tmp}" "${conv_script_file}"
# make the script executable
chmod +x $conv_script_file
# remove the link if it already exists (in case of reimport)
rm $script_file 2> /dev/null
# .. and re-create it as local symbolic link
# to the function in the imports dir
ln --symbolic --relative $conv_script_file $script_file
fi
printf "%s%20s: %-25s${COLOR_NONE}%s\n" "${log_action_color}" "${log_action}" "$a_name" "${log_content}"
done
done
Deprecated: Creating fish wrappers that execute bash code
Below is a script that creates fish script wrappers for the local bash aliases: For each bash alias, it takes the contents, and creates a fish alias/script that executes the code in bash sub-shell.
It is not optimal, but is sufficient for most of my aliases.
WARNING It might happen that the imported function acts differently then in bash. You may loose data or accidentally DDOS your coworkers when using them.
use like so:
# imports (or reimports) all bash aliases into fish functions, permanently
import_bash_aliases
# removes all fish functions previously imported by this script
import_bash_aliases clean
save this in ~/.config/fish/functions/import_bash_aliases.fish:
#!/usr/bin/fish
# Fish function to import bash aliases
#
# Copyright 2018 <hoijui.quaero#gmail.com>, licensed under the GPL-3.0+
#
# This script is based on a script from Malte Biermann,
# see: https://glot.io/snippets/efh1c4aec0
#
# WARNING: There is no guarantee that the imported aliases work the same way
# as they do in bash, so be cautious!
#
# Usage:
# import_bash_aliases # imports (or reimports) all bash aliases into fish functions, permanently
# import_bash_aliases clean # removes all fish functions previously imported by this script from bash aliases
function import_bash_aliases --description 'Converts bash aliases to .fish functions.\nThis might be called repeatedly, and will not override functions that are already defined in fish, except they are merely an older import from this script.'
set -l FISH_FUNCTIONS_DIR ~/.config/fish/functions
set -l BASH_IMPORTS_DIR_NAME bash-imports
set -l BASH_IMPORTS_DIR $FISH_FUNCTIONS_DIR/$BASH_IMPORTS_DIR_NAME
set -l README_FILE $BASH_IMPORTS_DIR/README.md
if test "$argv[1]" = "clean"
for fun_file in (find $FISH_FUNCTIONS_DIR -maxdepth 1 -name '*.fish')
set -l imp_fun_file $BASH_IMPORTS_DIR/(basename $fun_file)
if test -e $imp_fun_file ; and test (readlink --canonicalize $fun_file) = (realpath $imp_fun_file)
set -l fun_name (basename $fun_file '.fish')
echo "removing imported bash alias/function $fun_name"
rm $imp_fun_file
and rm $fun_file
and functions --erase $fun_name
end
end
rm $README_FILE ^ /dev/null
rmdir $BASH_IMPORTS_DIR ^ /dev/null
return 0
end
set -l SOURCE_FILES ~/.bashrc ~/.bash_aliases
mkdir -p $BASH_IMPORTS_DIR
echo -e "# Bash alias imports\n\nsee `$argv[0]`\n\nWARNING: Do NOT manually edit files in this directory. instead, copy them to $FISH_FUNCTIONS_DIR (replacing the symbolic link that already exists there), and edit that new file.\nIf you edit the files in this dir, it will be replaced on the next (re)import from bash aliases." \
> $README_FILE
set -l UNUSED_STUB_MSG "The bash alias corresponding to this function was NOT imported, because a corresponding function already exists at %s\n"
set -l AUTO_IMPORT_WARNING "# WARNING Do NOT edit this file by hand, as it was auto-generated from a bash alias, and may be overwritten in the future. please read {$README_FILE}"
function _fish_func_exists
set -l fun_name $argv[1]
# This also detects in-memory functions
functions --query $fun_name
# This also detects script files in the functions dir
# that do not contain a function wiht the same name
or test -e "$FISH_FUNCTIONS_DIR/$fun_name.fish"
return $status
end
function _is_link_to
set -l file_link $argv[1]
set -l file_target $argv[2]
test -e $file_target
and test (readlink --canonicalize $file_link) = (realpath $file_target)
return $status
end
for source_file in $SOURCE_FILES
for a in (cat $source_file | grep "^alias")
set -l a_name (echo $a | sed -e 's/alias \([^=]*\)=.*/\1/')
set -l a_command (echo $a | sed -e 's/alias \([^=]*\)=//' -e 's/[ \t]*#[^\'\"]\+$//')
set -l fun_file "$FISH_FUNCTIONS_DIR/$a_name.fish"
set -l imp_fun_file "$BASH_IMPORTS_DIR/$a_name.fish"
# Check whether the function already exists.
# If so, we skip importing it, unless it is just a link to a previously imported function.
if _fish_func_exists $a_name; and not _is_link_to $fun_file $imp_fun_file
set_color red
printf "%20s: %-25s\n" "skipping (exists)" $a_name
set_color normal
#printf $UNUSED_STUB_MSG $fun_file > $imp_fun_file
else
set_color green
printf "%20s: %-25s -> %s\n" "(re-)importing" $a_name $a_command
set_color normal
# remove the link, in case of re-importing
rm $fun_file ^ /dev/null
# write the function file
echo "#!/usr/bin/fish" > $imp_fun_file
echo "\
$AUTO_IMPORT_WARNING
function $a_name -d 'bash alias "$a_name" import'
bash -c $a_command' '\$argv''
end
" \
>> $imp_fun_file
# make the script executable
chmod +x $imp_fun_file
# .. and re-create it as local symbolic link
# to the function in the imports dir
ln --symbolic --relative $imp_fun_file $fun_file
end
end
end
# (re-)load all the functions we just defined
exec fish
end

How to pack jobs in bash and make output directory tree nested in parent?

After running this script I am getting job_1 directory inside job_0, job_2 directory inside job_1 etc I would like to have job_1,job_2,...job_63 directories inside just one directory job_0. What I need to change in my script in order for that to happen?
my code
#!/bin/bash
njobs=64
nprocs_per_job=32
let total_procs=($njobs * $nprocs_per_job)
sed 's/^.*width.*$/\#PBS -l mppwidth='$total_procs'/' qscript_test > tmp
mv tmp qscript
for (( i = 0; i < $njobs ; i++ ))
do
mkdir job_$i
cd job_$i
echo "cd job_$i" >> qscript
echo "aprun -n $nprocs_per_job python hello.py &" >> qscript
echo "cd .." >> qscript
done
echo "wait" >> qscript
qsub qscript
take some iterations of your loop as an example:
when i = 0
mkdir job_0; cd job_0
so now the working directory is job_0.
when i = 1
mkdir job_1; cd job_1
so now your dir structure is job0/job1 and your working directory is job1. mkdir job_2 will only create job_0/job_1/job_2 so this loop only nests directories.
you should first create job_0, cd into it and then proceed with the loop, adding to it that it should return to the previous directory in every iteration:
mkdir job_0
cd job_0 # change CWD to job_0
for (( i = 1; i < $njobs ; i++ )); do
mkdir job_$i
cd job_$i # change CWD to job_n
aprun -n $nprocs_per_job python hello.py >> qscript & # execute command in background and append the stdout to qscript file
cd .. # change CWD to job_0
done
cd .. # change CWD to the starting point
i'm limiting myself to change as little of your code as possible, however there are cleaner ways to do this, don't forget to check: pushd/popd
and/or subshells

How to get absolute path name of shell script on MacOS?

readlink -f does not exist on MacOS. The only working solution for Mac OS I managed to find on the net goes like this:
if [[ $(echo $0 | awk '/^\//') == $0 ]]; then
ABSPATH=$(dirname $0)
else
ABSPATH=$PWD/$(dirname $0)
fi
Can anyone suggest anything more elegant to this seemingly trivial task?
Another (also rather ugly) option:
ABSPATH=$(cd "$(dirname "$0")"; pwd -P)
From pwd man page,
-P Display the physical current working directory (all symbolic links resolved).
Get absolute path of shell script
Dug out some old scripts from my .bashrc, and updated the syntax a bit, added a test suite.
Supports
source ./script (When called by the . dot operator)
Absolute path /path/to/script
Relative path like ./script
/path/dir1/../dir2/dir3/../script
When called from symlink
When symlink is nested eg) foo->dir1/dir2/bar bar->./../doe doe->script
When caller changes the scripts name
It has been tested and used in real projects with success, however there may be corner cases I am not aware of.
If you were able to find such a situation, please let me know.
(For one, I know that this does not run on the sh shell)
Code
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]) do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd =[`pwd`]"
Known issuse
Script must be on disk somewhere, let it be over a network.
If you try to run this script from a PIPE it will not work
wget -o /dev/null -O - http://host.domain/dir/script.sh |bash
Technically speaking, it is undefined.
Practically speaking, there is no sane way to detect this.
Test case used
And the current test case that check that it works.
#!/bin/bash
# setup test enviroment
mkdir -p dir1/dir2
mkdir -p dir3/dir4
ln -s ./dir1/dir2/foo bar
ln -s ./../../dir3/dir4/test.sh dir1/dir2/foo
ln -s ./dir1/dir2/foo2 bar2
ln -s ./../../dir3/dir4/doe dir1/dir2/foo2
cp test.sh ./dir1/dir2/
cp test.sh ./dir3/dir4/
cp test.sh ./dir3/dir4/doe
P="`pwd`"
echo "--- 01"
echo "base =[${P}]" && ./test.sh
echo "--- 02"
echo "base =[${P}]" && `pwd`/test.sh
echo "--- 03"
echo "base =[${P}]" && ./dir1/dir2/../../test.sh
echo "--- 04"
echo "base =[${P}/dir3/dir4]" && ./bar
echo "--- 05"
echo "base =[${P}/dir3/dir4]" && ./bar2
echo "--- 06"
echo "base =[${P}/dir3/dir4]" && `pwd`/bar
echo "--- 07"
echo "base =[${P}/dir3/dir4]" && `pwd`/bar2
echo "--- 08"
echo "base =[${P}/dir1/dir2]" && `pwd`/dir3/dir4/../../dir1/dir2/test.sh
echo "--- 09"
echo "base =[${P}/dir1/dir2]" && ./dir1/dir2/test.sh
echo "--- 10"
echo "base =[${P}/dir3/dir4]" && ./dir3/dir4/doe
echo "--- 11"
echo "base =[${P}/dir3/dir4]" && ./dir3/dir4/test.sh
echo "--- 12"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/doe
echo "--- 13"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir3/dir4/test.sh
echo "--- 14"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/doe
echo "--- 15"
echo "base =[${P}/dir3/dir4]" && `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 16"
echo "base s=[${P}]" && source test.sh
echo "--- 17"
echo "base s=[${P}]" && source `pwd`/test.sh
echo "--- 18"
echo "base s=[${P}/dir1/dir2]" && source ./dir1/dir2/test.sh
echo "--- 19"
echo "base s=[${P}/dir3/dir4]" && source ./dir1/dir2/../../dir3/dir4/test.sh
echo "--- 20"
echo "base s=[${P}/dir3/dir4]" && source `pwd`/dir1/dir2/../../dir3/dir4/test.sh
echo "--- 21"
pushd . >/dev/null
cd ..
echo "base x=[${P}/dir3/dir4]"
./`basename "${P}"`/bar
popd >/dev/null
PurpleFox aka GreenFox
Also note that homebrew's (http://brew.sh) coreutils package includes realpath (link created in/opt/local/bin).
$ realpath bin
/Users/nhed/bin
Using bash I suggest this approach. You first cd to the directory, then you take the current directory using pwd. After that you must return to the old directory to ensure your script does not create side effects to an other script calling it.
cd "$(dirname -- "$0")"
dir="$PWD"
echo "$dir"
cd - > /dev/null
This solution is safe with complex path. You will never have troubles with spaces or special charaters if you put the quotes.
Note: the /dev/null is require or "cd -" print the path its return to.
If you don't mind using perl:
ABSPATH=$(perl -MCwd=realpath -e "print realpath '$0'")
Can you try something like this inside your script?
echo $(pwd)/"$0"
In my machine it shows:
/home/barun/codes/ns2/link_down/./test.sh
which is the absolute path name of the shell script.
I've found this to be useful for symlinks / dynamic links - works with GNU readlink only though (because of the -f flag):
# detect if GNU readlink is available on OS X
if [ "$(uname)" = "Darwin" ]; then
which greadlink > /dev/null || {
printf 'GNU readlink not found\n'
exit 1
}
alias readlink="greadlink"
fi
# create a $dirname variable that contains the file dir
dirname=$(dirname "$(readlink -f "$0")")
# use $dirname to find a relative file
cat "$dirname"/foo/bar.txt
this is what I use, may need a tweak here or there
abspath ()
{
case "${1}" in
[./]*)
local ABSPATH="$(cd ${1%/*}; pwd)/${1##*/}"
echo "${ABSPATH/\/\///}"
;;
*)
echo "${PWD}/${1}"
;;
esac
}
This is for any file - and of curse you can just invoke it as abspath ${0}
The first case deals with relative paths by cd-ing to the path and letting pwd figure it out
The second case is for dealing with a local file (where the ${1##/} would not have worked)
This does NOT attempt to undo symlinks!
This works as long as it's not a symlink, and is perhaps marginally less ugly:
ABSPATH=$(dirname $(pwd -P $0)/${0#\.\/})
If you're using ksh, the ${.sh.file} parameter is set to the absolute pathname of the script. To get the parent directory of the script: ${.sh.file%/*}
I use the function below to emulate "readlink -f" for scripts that have to run on both linux and Mac OS X.
#!/bin/bash
# This was re-worked on 2018-10-26 after der#build correctly
# observed that the previous version did not work.
# Works on both linux and Mac OS X.
# The "pwd -P" re-interprets all symlinks.
function read-link() {
local path=$1
if [ -d $path ] ; then
local abspath=$(cd $path; pwd -P)
else
local prefix=$(cd $(dirname -- $path) ; pwd -P)
local suffix=$(basename $path)
local abspath="$prefix/$suffix"
fi
if [ -e $abspath ] ; then
echo $abspath
else
echo 'error: does not exist'
fi
}
# Example usage.
while (( $# )) ; do
printf '%-24s - ' "$1"
read-link $1
shift
done
This is the output for some common Mac OS X targets:
$ ./example.sh /usr/bin/which /bin/which /etc/racoon ~/Downloads
/usr/bin/which - /usr/bin/which
/bin/which - error: does not exist
/etc/racoon - /private/etc/racoon
/Users/jlinoff/Downloads - /Users/jlinoff/Downloads
The is the output for some linux targets.
$ ./example.sh /usr/bin/which /bin/whichx /etc/init.d ~/Downloads
/usr/bin/which - /usr/bin/which
/bin/whichx - error: does not exist
/etc/init.d - /etc/init.d
/home/jlinoff/Downloads - /home/jlinoff/Downloads

Resources