I'm running it under MacOS El Capitan 10.10.6
among all commands to get my current dir (path I'm running my script from) only this works for me:
FILES="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
But it's not going to work if the folder has whitespace in it's name (aka: "folder name")
How to fix this?
Thank you! )
Update: added a script:
#!/bin/bash
function check ()
{
oldsize=`wc -c <"$1"`
sleep 1
newsize=`wc -c <"$1"`
while [ "$oldsize" -lt "$newsize" ]
do
echo "Not yet..."
oldsize=`wc -c <"$1"`
sleep 1
newsize=`wc -c <"$1"`
done
if [ "$oldsize" -eq "$newsize" ]
then
echo "The file has been copied completely."
fi
}
FILES="$(dirname "${BASH_SOURCE[0]}")/*"
function main
{
for f in $FILES
do
if [[ "$f" =~ \.mkv$ ]];
then
#/////////////////////////////////////////////////////////////////////
check "$f"
(( count = count + 1 ))
g="${f/mkv/avi}"
#LOG_FILE="${g/avi/log}"
#exec > >(tee -a "${LOG_FILE}" )
#exec 2> >(tee -a "${LOG_FILE}" >&2)
now="$(date)"
printf "Current date and time %s\n" "$now"
echo "Processing $f file..."
#avconv -i "${f}" -map 0:0 -map 0:1 -codec copy -sn "${g}"
avconv -i "$f" -map 0 -codec copy "$g"
if [ $? -eq 0 ]; then
echo OK
rm "$f"
else
echo FAIL
rm "$g"
#rm "$LOG_FILE"
return
fi
fi
#/////////////////////////////////////////////////////////////////////
done
}
############
count=0
############
main
if (($count > 0)); then
open "$(dirname "${BASH_SOURCE[0]}")"
fi
exit
I am using Mac OS X 10.11.6, and I have a directory $HOME/tmp. From there, I executed:
$ cd $HOME/tmp
$ pwd
/Users/jleffler/tmp
$ mkdir -p "Spaced Out Directory "/bin
$ export PATH="$PATH:$PWD/$_"
$ cat <<'EOF' > Spaced\ Out\ \ Directory\ \ \ /bin/gorblinsky.sh
> #!/bin/bash
>
> echo "PWD=$PWD"
> DIR="$(dirname "${BASH_SOURCE[0]}")"
> echo "DIR=$DIR"
> cd "$DIR"
> pwd
> echo "PWD=$PWD"
> EOF
$ chmod +x Spaced\ Out\ \ Directory\ \ \ /bin/gorblinsky.sh
$ gorblinsky.sh
PWD=/Users/jleffler/tmp
DIR=/Users/jleffler/tmp/Spaced Out Directory /bin
/Users/jleffler/tmp/Spaced Out Directory /bin
PWD=/Users/jleffler/tmp/Spaced Out Directory /bin
$
This shows that the command $(dirname "${BASH_SOURCE[0]}") can determine the name of the directory where the source for the command is stored.
If the script was going to use the variable $DIR to specify file names, you'd need to be careful (very careful) to ensure it is always properly quoted.
For example:
cp "$DIR/gorblinksky.h" "$HOME/tmp/cobbled together name"
Modern style is to always (double) quote all variable references, even when there's nothing in them that needs protecting (see shellcheck.net for example — and Google Shell Style Guide). I'm old-school enough not to put quotes around names that can't contain spaces or metacharacters, but I guess that is just old-fashioned. For example, I shell-checked a script for playing with RCS version numbers, and it doesn't quote variables containing dotted strings of digits (9.19.2.24 — could be an IBM IPv4 address too) and I was told off for not quoting them, though the file names were already protected with quotes.
Related
Good day,
I need your help in creating next script
Every day teacher uploading files in next format:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx
STUDENT_MARKS_20200217074343-20200217.xlsx
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx
[file_name+todaydatetime-todaydate.xlsx]
But sometimes a teacher is not uploading these files and we need to do manual renaming the files received for the previous date and then copying every separate file to separate folder like:
cp STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
cp STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/STUDENT_ACCOUNTS_20200318074343-20200318.xlsx
.............
cp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_20200318074343-20200318.xlsx.
In two words - taking the files from previous date copying them to specific folder with a new timestamp.
#!/bin/bash
cd /home/incoming/
date=$(date '+%Y%m%d')
previousdate="$( date --date=yesterday '+%Y%m%d' )"
cp /home/incoming/SUBSCRIBED_STUDENTS_'$previousdate'.xlsx /incoming/F1/SUBSCRIBED_STUDENTS_'$date'.xlsx
and there could be case when teacher can upload one file and others not, how to do check for existing files?
Thanks for reading that, if you can help me i will ne really thankful - you will save plenty of manual work for me.
The process can be automated completely if your directory structure is known. If it follows some kind of pattern, do mention it here.
For the timing, this maybe helpful:
Filename "tscp"
#
# Stands for timestamped cp
#
tscp() {
local file1=$1 ; shift
local to_dir=$1 ; shift
local force_copy=$1 ; shift
local current_date="$(date '+%Y%m%d')"
if [ "${force_copy}" == "--force" ] ; then
cp "${file1}" "${to_dir}/$(basename ${file1%-*})-${current_date}.xlsx"
else
cp -n "${file1}" "${to_dir}/$( basename ${file1%-*})-${current_date}.xlsx"
fi
}
tscp "$#"
It's usage is as follows:
tscp source to_directory [-—force]
Basically the script takes 2 arguments and the 3rd one is optional.
First arg is source file path and second are is the directory path to where you want to copy (. if same directory).
By default this copy would be made if and only if destination file doesn't exist.
If you want to overwrite the destination file then pass a third arg —force.
Again, this can be refined much much more based on details provided.
Sample usage for now:
bash tscp SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/F1/
will copy SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx to directory /incoming/F1/ with updated date if it doesn't exist yet.
UPDATE:
Give this a go:
#! /usr/bin/env bash
printf_err() {
ERR_COLOR='\033[0;31m'
NORMAL_COLOR='\033[0m'
printf "${ERR_COLOR}$1${NORMAL_COLOR}" ; shift
printf "${ERR_COLOR}%s${NORMAL_COLOR}\n" "$#" >&2
}
alias printf_err='printf_err "Line ${LINENO}: " '
shopt -s expand_aliases
usage() {
printf_err \
"" \
"usage: ${BASH_SOURCE##*/} " \
" -f copy_data_file" \
" -d days_before" \
" -m months_before" \
" -o" \
" -y years_before" \
" -r " \
" -t to_dir" \
>&2
exit 1
}
fullpath() {
local path="$1" ; shift
local abs_path
if [ -z "${path}" ] ; then
printf_err "${BASH_SOURCE}: Line ${LINENO}: param1(path) is empty"
return 1
fi
abs_path="$( cd "$( dirname "${path}" )" ; pwd )/$( basename ${path} )"
printf "${abs_path}"
}
OVERWRITE=0
REVIEW=0
COPYSCRIPT="$( mktemp "/tmp/copyscriptXXXXX" )"
while getopts 'f:d:m:y:t:or' option
do
case "${option}" in
d)
DAYS="${OPTARG}"
;;
f)
INPUT_FILE="${OPTARG}"
;;
m)
MONTHS="${OPTARG}"
;;
t)
TO_DIR="${OPTARG}"
;;
y)
YEARS="${OPTARG}"
;;
o)
OVERWRITE=1
;;
r)
REVIEW=1
COPYSCRIPT="copyscript"
;;
*)
usage
;;
esac
done
INPUT_FILE=${INPUT_FILE:-$1}
TO_DIR=${TO_DIR:-$2}
if [ ! -f "${INPUT_FILE}" ] ; then
printf_err "No such file ${INPUT_FILE}"
usage
fi
DAYS="${DAYS:-1}"
MONTHS="${MONTHS:-0}"
YEARS="${YEARS:-0}"
if date -v -1d > /dev/null 2>&1; then
# BSD date
previous_date="$( date -v -${DAYS}d -v -${MONTHS}m -v -${YEARS}y '+%Y%m%d' )"
else
# GNU date
previous_date="$( date --date="-${DAYS} days -${MONTHS} months -${YEARS} years" '+%Y%m%d' )"
fi
current_date="$( date '+%Y%m%d' )"
tmpfile="$( mktemp "/tmp/dstnamesXXXXX" )"
awk -v to_replace="${previous_date}" -v replaced="${current_date}" '{
gsub(to_replace, replaced, $0)
print
}' ${INPUT_FILE} > "${tmpfile}"
paste ${INPUT_FILE} "${tmpfile}" |
while IFS=$'\t' read -r -a arr
do
src=${arr[0]}
dst=${arr[1]}
opt=${arr[2]}
if [ -n "${opt}" ] ; then
if [ ! -d "${dst}" ] ;
then
printf_err "No such directory ${dst}"
usage
fi
dst="${dst}/$( basename "${opt}" )"
else
if [ ! -d "${TO_DIR}" ] ;
then
printf_err "No such directory ${TO_DIR}"
usage
fi
dst="${TO_DIR}/$( basename "${dst}" )"
fi
src=$( fullpath "${src}" )
dst=$( fullpath "${dst}" )
if [ -n "${OVERWRITE}" ] ; then
echo "cp ${src} ${dst}"
else
echo "cp -n ${src} ${dst}"
fi
done > "${COPYSCRIPT}"
if [ "${REVIEW}" -eq 0 ] ; then
${BASH} "${COPYSCRIPT}"
rm "${COPYSCRIPT}"
fi
rm "${tmpfile}"
Steps:
Store the above script in a file, say `tscp`.
Now you need to create the input file for it.
From you example, a sample input file can be like:
STUDENT_ACCOUNTS_20200217074343-20200217.xlsx /incoming/A1/
STUDENT_MARKS_20200217074343-20200217.xlsx /incoming/B1/
STUNDENT_HOMEWORKS_20200217074343-20200217.xlsx
STUDENT_PHYSICAL_20200217074343-20200217.xlsx
SUBSCRIBED_STUDENTS_20200217074343-20200217.xlsx /incoming/FI/
Where first part is the source file name and after a "tab" (it should be a tab for sure), you mention the destination directory. These paths should be either absolute or relative the the directory where you are executing the script. You may not mention destination directory if all are to be sent to same directory (discussed later).
Let's say you named this file `file`.
Also, you don't really have to type all that. If you have these files in the current directory, just do this:
ls -1 > file
(the above is ls "one", not "l".)
Now we have the `file` from above in which we didn't mention destination directory for all but only for some.
Let's say we want to move all other directories to `/incoming/x` and it exists.
Now script is to be executed like:
bash tscp -f file -t /incoming/x -r
Where `/incoming/x` is the default directory i.e. when none other directory is mentioned in `file`, your files are moved to this directory.
Now in the current directory a script named `copyscript` will be generated which will contain `cp` commands to copy all files. You can open a review `copyscript` and if the copying seems right, go ahead and:
bash copyscript
which will copy all the files and then you can:
rm copyscript
You need not generate to `copyscript` and can straight away go for a copy like:
bash tscp -f file -t /incoming/x
which won't generate any copyscript and copy straight away.
Previously `-r` caused the generation of `copyscript`.
I would recomment to use version with `-r` because that is a little safer and you will be sure that right copies are being made.
By default it would check for the previous day and rename to current date, but you can override that behaviour as:
bash tscp -f file -t /incoming/x -d 3
`-d 3` would look for 3 days back files in `file`.
By default copies won't overwrite i.e. if file at the destination already exists, copies won't be made.
If you want to overwrite, add flag `-o`.
As a conclusion I would advice to use:
bash tscp -f file -r
where file contains tab separated values like above for all.
Also, adding tscp to path would be a good idea after you are sure it works ok.
Also the scipt is made on mac and there is always a change of version clash of tools used. I would suggest to try the script on some sample data first to make sure script works right on your machine.
I'm using this code in a bash script. I use it to transfer a source folder to multiple destinations:
cd /Volumes/ ; tar cf - SOURCE/ | tee \
>( cd /Volumes/dest1 ; tar xf - ) \
>( cd /Volumes/dest2 ; tar xf - ) \
> /dev/null
This command works well. I want to set the destinations at the beginning of the script. So the number of destinations can vary.
For example the destinations can be in a var or an array:
destinationList=/Volumes/dest1 /Volumes/dest2
cd /Volumes/Untitled/ ; tar cf - SOURCE/ | tee \
# for item in destinationList
# do
# add this code ">( cd $item ; tar xf - )"
# done
> /dev/null
Is there a nice way to do it?
This is a case where eval is one of the easier options, though it needs to be very used very carefully.
unpackInDestinations() {
local dest currArg='' evalStr=''
for dest; do
printf -v currArg '>(cd %q && exec tar xf -)' "$dest"
evalStr+=" $currArg"
done
eval "tee $evalStr >/dev/null"
}
tar cf - SOURCE/ | unpackInDestinations /Volumes/dest{1,2}
Less efficiently (but without, perhaps, causing anyone trying to audit the code's security as much consternation), one can also write a recursive function:
unpackInDestinations() {
local dest
if (( $# == 0 )); then
cat >/dev/null
elif (( $# == 1 )); then
cd "$1" && tar xf -
else
dest=$1; shift
tee >(cd "$dest" && exec tar xf -) | unpackInDestinations "$#"
fi
}
The number of tees this creates varies with the number of arguments, so it's substantially less efficient than the hand-written code or the eval-based equivalent to same.
If you only need to support new versions of bash (the below requires at least 4.1), there's some additional magic available that can provide the best of both worlds:
unpackInDestinations() {
local -a dest_fds=( ) args=( )
local arg fd_num retval
# open a file descriptor for each argument
for arg; do
exec {fd_num}> >(cd "$arg" && exec tar xf -)
dest_fds+=( "$fd_num" )
args+=( "/dev/fd/$fd_num" )
done
tee "${args[#]}" >/dev/null; retval=$?
# close the FDs
for fd_num in "${dest_fds[#]}"; do
exec {fd_num}>&-
done
# and return the exit status we got from tee
return "$retval"
}
I have the following problem. I need to create system-wide JDK_HOME and JAVA_HOME variables. First I want to create /etc/profile.d/java.sh and add
JDK_HOME to it. Then I want to append JAVA_HOME to this file. So far I have this code.
#!/bin/bash
create_env_var()
{
local varname="$1"
local varvalue="$2"
local filename="/etc/profile.d/$3"
if [ -e "$filename" ]; then
echo "**ERROR: file $filename already exists"
else
sh -c 'echo "$varname=$varvalue" > $filename'
chmod +x "$filename"
fi
}
append_env_var()
{
local varname="$1"
local varvalue="$2"
local filename="/etc/profile.d/$3"
if [ ! -e "$filename" ]; then
echo "**ERROR: file $filename not found"
else
sh -c 'echo "$varname=$varvalue" >> $filename'
chmod +x "$filename"
fi
}
create_env_var "JDK_HOME" "/usr/lib/jvm/java-7-openjdk-i386" "java.sh"
append_env_var "JAVA_HOME" "/usr/lib/jvm/java-7-openjdk-i386" "java.sh"
exit "$?"
However these lines don't work and I see the following errors:
sh: 1: cannot create : Directory nonexistent
chmod: cannot access ‘/etc/profile.d/java.sh’: No such file or directory
Would you please show me where everything goes wrong?
While you can remove the single quotes, there's no reason to create a subprocess and use sh -c.
change sh -c 'echo "$varname=$varvalue" > $filename'
to echo "$varname=$varvalue" > $filename
and sh -c 'echo "$varname=$varvalue" >> $filename'
to echo "$varname=$varvalue" >> $filename
I need help with Ubuntu Precise bash script.
I have several tiff files in various folders
masterFOlder--masterSub1 --masterSub1-1 --file1.tif
|--masterSub1-2 --masterSub1-2-1 --file2.tif
|
|--masterSub2 --masterSub1-2 .....
I need to run an Imagemagick command and save them to new folder "converted" while retaining the sub folder tree i.e. the new tree will be
converted --masterSub1 --masterSub1-1 --file1.png
|--masterSub1-2 --masterSub1-2-1 --file2.png
|
|--masterSub2 --masterSub1-2 .....
How do i split the filepath into folders, replace the first folder (masterFOlder to converted) and recreate a new file path?
Thanks to everyone reading this.
This script should work.
#!/bin/bash
shopt -s extglob && [[ $# -eq 2 && -n $1 && -n $2 ]] || exit
MASTERFOLDER=${1%%+(/)}/
CONVERTFOLDER=$2
OFFSET=${#MASTERFOLDER}
while read -r FILE; do
CPATH=${FILE:OFFSET}
CPATH=${CONVERTFOLDER}/${CPATH%.???}.png
CDIR=${CPATH%/*}
echo "Converting $FILE to $CPATH."
[[ -d $CDIR ]] || mkdir -p "$CDIR" && echo convert "$FILE" "$CPATH" || echo "Conversion failed."
done < <(exec find "${MASTERFOLDER}" -mindepth 1 -type f -iname '*.tif')
Just replace echo convert "$FILE" "$CPATH" with the actual command you use and run bash script.sh masterfolder convertedfolder
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