LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   bash script - return full path and filename (https://www.linuxquestions.org/questions/programming-9/bash-script-return-full-path-and-filename-680368/)

Andy Alt 10-31-2008 07:02 PM

bash script - return full path and filename
 
I'm attempting to improve a bash script, ReMove to Waste. It moves files into ~/.Waste and prepends the present working directory to the destination folder under ~/.Waste.

I'd like to have it moved to the same path from where the file or folder originate.
For instance, if I'm in my home directory, and delete a file within it called "temp," it will move it to ~/.Waste/home/andy/temp

So that is indeed where the file is supposed to go, but it's basing it on my PWD at the time.

I'd like to return the full path of the file name. But whatever PWD I'm in, the best I can get is "./temp"

basename and dirname are the opposite of what I need.
I tried ${FILE%} but that also returns "./temp"

I've been googling for over an hour, but getting nowhere.

Thanks, in advance.

jailbait 10-31-2008 07:08 PM

Use $PWD as a prefix to the file name.

---------------------
Steve Stites

Andy Alt 10-31-2008 07:28 PM

I'd like to be able to move the file into ~/.Waste/original/path

Using $PWD doesn't guarantee that. I need to have the absolute path of the file returned.

(a few minutes later)
Found it.
Bash equivalent for PHP realpath()

nishamathew1980 11-02-2008 05:04 AM

Wow ... That's something new I learnt today. Thanks for pointing me to the article - http://andy.wordpress.com/2008/05/09...-php-realpath/

:)

Linux Archive

Andy Alt 11-04-2008 01:19 AM

Quote:

Originally Posted by nishamathew1980 (Post 3328956)
Wow ... That's something new I learnt today. Thanks for pointing me to the article - http://andy.wordpress.com/2008/05/09...-php-realpath/

:)

I noticed using it in a reMoval script can be a little hazardous. It won't move the symbolic link, it goes after the source file instead. Be careful!

Disillusionist 11-04-2008 01:50 AM

Code:

source="./tmp/myfile"
dest=~/.Waste/$(echo $source|sed "s@^./@$PWD@"|sed "s@^/@@")


Andy Alt 06-19-2009 02:29 PM

Linux console recycle bin
 
Disillusionist, thanks, but unfortunately, I don't know what your code does. Regular expressions confuse me greatly (though I'm now starting to realize how useful sed can be and that I need to learn how to use it), and I saw PWD in there; using the present working directory was something I was trying to avoid.

Here's the script so far, for anyone interested. The -u option will undo the last operation performed. This is the version currently in the subversion repo at http://sourceforge.net/projects/rmw

I won't be doing anything more with it for a few weeks or month, but if anyone has any suggestions for improvements, I'll most likely implement them at some point.

Code:

#!/bin/sh
# rmw - ReMove to Waste
# Author: Andy Alt ( https://sourceforge.net/projects/rmw/ )
# May 30, 2009
# License: GPL
# Description: ReMove to Waste, rmw, is a bash script.
# It functions as a Linux/Unix console recycle bin when
# reMoving files with rm. ReMove to Waste will move files
# to a Waste folder or "Trash can," and it will prepend
# the time and date to files when reMoving them. It keeps a
# "history" to easily copy files back to their previous location.
#
# WARNING: Use this script at your own risk.

# Change this to set the location of the rmw configuration directory
RMWCONF=${HOME}/.rmw

debug=1
# Other variables can be found in the file rmwrc (which will be created
# after this script has been run once) in your rmw config directory

displayver() {
        echo -e "\nReMove to Waste $VERSION\n"
}

displayhelp() {

echo usage: -i / interactive
echo -v / be verbose
echo -u / undo-last ReMove
echo -f / force, no prompting
 }

VERSION=0.97-svn-2009May30

if [ $# -eq 0 ]; then
        echo "No options passed"
        exit 1
fi

mvargs="-"

while getopts ":ivfuh" Option
do

case $Option in
    i    ) mvargs=$mvargs"i";;
    v    ) mvargs=$mvargs"v";;
#    v    ) verbosity=1;;
    f    ) mvargs=$mvargs"f";;
#    v    ) echo $VERSION;;
    u        ) doundo=1;;
    h        ) displayhelp;;
#    *    ) echo "Unimplemented option chosen.";;  # DEFAULT
  esac
done

shift $(($OPTIND - 1))

if [ $mvargs == "-" ]; then
        mvargs=""
fi

        if [ $doundo ]; then
                source ${RMWCONF}/undofile-last
                exit 0
        fi

#if [ $debug = 1 ]; then
       
#fi



# Create config dir if it doesn't exist
if [ ! -d ${RMWCONF} ]; then
        echo ""
        mkdir -pv ${RMWCONF}
       
        if [ ! -d ${RMWCONF} ]; then
                echo "Unable to read/write configuration directory!*"
                exit 1
        fi
fi

# Create configuration file if it doesn't exist
#
#
if [ ! -e ${RMWCONF}/rmwrc ]; then
echo ""
echo "Creating rmw configuration file ${RMWCONF}/rmwrc"
#cat > ${RMWCONF}/rmwrc << "EOF"
cat > ${RMWCONF}/rmwrc << "EOF"
# The following two lines added by rmw installation script
WASTE=${HOME}/.Waste
# After running rmw x times, disk usage of Waste directory will
# be displayed. Use 0 to disable
showdu=10
#
# Number of undo files to keep
num_undofiles=20
#
# Use verbosity even if it's not passed on the command line
# 0 to disable
verbosity=1
EOF
fi

# Read in variables from configuration file
source ${RMWCONF}/rmwrc
#


if [ ! $num_undofiles ]; then
cat >> ${RMWCONF}/rmwrc << "EOF"
# Number of undo files to keep
num_undofiles=20
EOF
num_undofiles=20
fi

if [ ! $verbosity ]; then
cat >> ${RMWCONF}/rmwrc << "EOF"
#
# Use verbosity even if it's not passed on the command line
# 0 to disable
verbosity=1
EOF
verbosity=1
fi

if [ $verbosity = 1 ]; then
        mvargs=$mvargs" -v"
fi

if [ -e ${RMWCONF}/UndoFilesCounter ]; then
       
        undofile_count=`cat ${RMWCONF}/UndoFilesCounter`
       
        else

        echo 0 > ${RMWCONF}/UndoFilesCounter
        undofile_count=1
       
        fi

if [ $undofile_count -gt $num_undofiles ]; then

        undofile_count=1

        else
       
        ((undofile_count++))

fi

echo $undofile_count > ${RMWCONF}/UndoFilesCounter

UNDO_FILE=${RMWCONF}/undofile-${undofile_count}

# BUG - This will overwrite an UNDO file even if rmw
# is being executed without filenames to ReMove
#
echo "" > $UNDO_FILE

displayver


# The mv command has a --suffix option, but I chose to
# prepend the time and date. That keeps the file extension
# intact, and thus, if desired later, more easily opened
# using a file manager
#
# date --help for more details
#
PREPENDSTR=`date +%Fh%Hm%Ms%S`

        for FILE in "${@}" ; do

        if [ -e "${FILE}" ]; then
                filecounter=1

# -h tests whether or not the file is a symlink.
# See ChangeLog (May 25, 2009) for more details.
# readlink will ReMove the source file, so if the
# file is a symlink, the readlink/abs path section
# will be skipped.

                if [ ! -h "${FILE}" ]; then

# readlink finds the absolute path of the file being reMoved.
# I use the absolute path (instead of present working directory)
# so we'll know where the file was originally located.
                ABSFILEPATH=`readlink -f "${FILE}"`
                ABSPATH=$(dirname "${ABSFILEPATH}")
                DEST=${WASTE}${ABSPATH}
                BNAME=`basename "${ABSFILEPATH}"`

# I should probably change the $DEST var to $DESTDIR
                if [ ! -d "${DEST}" ]; then
                        mkdir -vp "${DEST}"
                fi

# The three lines below write to an undo file
#
echo "if [ ! -d \"${ABSPATH}\" ]; then" >> ${UNDO_FILE}
echo "mkdir -vp \"${ABSPATH}\"" >> ${UNDO_FILE}

echo "fi" >> ${UNDO_FILE}


                        if [ ! -e "${DEST}"/"${BNAME}" ]; then
                                mv $mvargs "${ABSFILEPATH}" "${DEST}"/"${BNAME}"
                                echo mv $mvargs \""${DEST}"/"${BNAME}"\" \""${ABSFILEPATH}"\" >> $UNDO_FILE

                               
                        else
                                mv $mvargs "${ABSFILEPATH}" "${DEST}"/"${PREPENDSTR}"_"${BNAME}"
                                echo mv $mvargs \""${DEST}"/"${PREPENDSTR}"_"${BNAME}"\" \""${ABSFILEPATH}"\" >> $UNDO_FILE

                        fi

# If statement commented, I don't think it's necessary
# anymore.
#
#                if [ ! -n "$mvargs" ]; then
                        echo "ReMoved ${FILE}"
#                fi

# This is what happens to symlinks

# Removal of symlinks are not yet added to the undo file
                else

                        mkdir -pv "${WASTE}""${PWD}"
                        mv -iv "${FILE}" "${WASTE}""${PWD}"

                fi


        fi
       


        done

if [ ! -e ${RMWCONF}/timesrun ]; then
        echo 1 > ${RMWCONF}/timesrun
fi


if [ $showdu -ne 0 ];then

        timesrun=`cat ${RMWCONF}/timesrun`

        if [[ $timesrun -ge $showdu ]];
                then echo 1 > ${RMWCONF}/timesrun
                echo ""
                echo "Disk usage for Waste folder:"
                du -sh $WASTE

                elif [ $showdu -gt 0 ];then

                ((timesrun++))
                echo $timesrun > ${RMWCONF}/timesrun
        fi
fi


# Can we figure out how to empty the Waste automatically
# if directory usage exceeds x number of bytes?
#
#echo !`du -s $WASTE`!
#if [ `du -s $WASTE` gt 64000 ]; then
#        echo "Deleting Waste"
#echo "greater than 64000"
#        /bin/rm -ir $WASTE
#fi

echo ""

# If any files were ReMoved, copy the Undo file to undofile-last
# for use with the --undo-last option
# If we didn't ReMove any files, this would copy an old undo file
# to undofile-last, some of those files may not exist anymore, or we
# we wouldn't want them restored.
# Using the filecounter var to make sure that doesn't happen
#


if [[ -e $UNDO_FILE && $filecounter ]]; then
        cp -v --remove-destination $UNDO_FILE ${RMWCONF}/undofile-last
        echo ""
fi


exit 0


# end script


Disillusionist 06-19-2009 04:37 PM

Quote:

Originally Posted by Andy Alkaline (Post 3579959)
Disillusionist, thanks, but unfortunately, I don't know what your code does. Regular expressions confuse me greatly (though I'm now starting to realize how useful sed can be and that I need to learn how to use it), and I saw PWD in there; using the present working directory was something I was trying to avoid.

What is your concern with using $PWD? If concerned about someone exporting variables to an incorrect value, you could always use $(pwd) to set the value of PWD.

The important part of the code I suggested, passed the value of $source through two sed statements.:
Code:

echo $source|sed "s@^./@$PWD@"|sed "s@^/@@"
The first sed statement looked was to replace any instances of ./ at the start of the variable with $PWD

The second sed statement removes the leading / character

Andy Alt 06-19-2009 06:00 PM

Quote:

Originally Posted by Disillusionist (Post 3580019)
What is your concern with using $PWD? If concerned about someone exporting variables to an incorrect value, you could always use $(pwd) to set the value of PWD.

My only concern is that I want rmw'ed files to go to ~/.Waste/original/path. In most cases $PWD would be fine, but if I'm removing files from /tmp or ~/Documents while my pwd is /mnt/cdrom, for example, they wouldn't be quite as easy to "restore" later.

I'm still using $PWD for symlinks. I'm using 'readlink' to determine absolute paths, and it always returns the source of the symlink, which I don't want.

Disillusionist 06-20-2009 03:24 AM

The sed statement would only use PWD where the source file begins with ./

Any files that begin with ./ are by definition starting from the current working directory.

EDIT:

Your script (downloaded from sourceforge) needs to run as #!/bin/bash on my machine (although I suspect this is the same on others) as source is a shell builtin for bash, but doesn't appear to be for traditional bourne shell. -- I am using Jaunty (Ubuntu 9.04)

Andy Alt 06-20-2009 03:06 PM

Thanks, I made the change and issued a new file release (made some other changes too).

On Slackware, which is my primary system, /bin/sh is a symlink to /bin/bash.

ta0kira 06-20-2009 03:51 PM

Try this (where $file is e.g. ./temp):
Code:

echo "$( readlink -f "$( dirname "$file" )" )/$( basename "$file" )"
Kevin Barry

Disillusionist 06-21-2009 04:34 AM

Quote:

Originally Posted by Andy Alkaline (Post 3580795)
Thanks, I made the change and issued a new file release (made some other changes too).

On Slackware, which is my primary system, /bin/sh is a symlink to /bin/bash.

New package is missing an install.sh file

It would also be nice to see an uninstall.sh file to simplify removal.

EDIT:
As rm is linked to rmw, you need rmw to cater for all the options that rm can use, especially rm -rf

vonbiber 06-21-2009 05:51 AM

Quote:

Originally Posted by Andy Alkaline (Post 3327741)
I'm attempting to improve a bash script, ReMove to Waste. It moves files into ~/.Waste and prepends the present working directory to the destination folder under ~/.Waste.

I'd like to have it moved to the same path from where the file or folder originate.
For instance, if I'm in my home directory, and delete a file within it called "temp," it will move it to ~/.Waste/home/andy/temp

So that is indeed where the file is supposed to go, but it's basing it on my PWD at the time.

I'd like to return the full path of the file name. But whatever PWD I'm in, the best I can get is "./temp"

basename and dirname are the opposite of what I need.
I tried ${FILE%} but that also returns "./temp"

I've been googling for over an hour, but getting nowhere.

Thanks, in advance.

ok, if I understood correctly you have a bash script
(say, move2waste.sh) that is
in your $PATH, perhaps in /usr/local/bin, and when
you

move2waste.sh some_file

where some_file is in a certain directory /some/path

you want some_file to be moved to /some/path/.Waste

(provided you have the right permissions in /some/path)

I would go like that (I'll skip the permissions verifications,
the number of arguments, etc.)

#!/bin/sh

# here you loop for each argument entered,
# verifying that the file exists, etc.
# say the file to be processed is in $filename
# $filename is a basename (no preceding directories:
# you invoke the script in the directory where
# you want to move files)

CWD=$PWD
# create .Waste directory if it doesn't already exist
mkdir -p $CWD/.Waste

mv $filename $CWD/.Waste

##################################

if you want to invoke the script move2waste.sh with
arguments such as
/absolute/path/to/filename
or
relativepath/to/filename

you should, when you process these arguments, retrieve the
the part before the filename
(e.g., argument=/some/path/filename
p=${argument%/*})
then figure out if it's a relative or absolute path:
if it starts with '/' it's absolute, otherwise relative
('.', '[a-zA-Z0-9]', ...)
if it's absolute you set CWD to that: CWD=$p
if it's relative:
if it starts with './' you remove that (p=$(echo $p | sed 's?^\./??'),
if it starts with '../', you get the name of the directory above ...
then you set CWD to $CWD/$p

Andy Alt 06-21-2009 01:40 PM

I've been using readlink for a few months now, so I'm no longer in need of methods to use absolute paths.

Quote:

Originally Posted by Disillusionist (Post 3581145)
New package is missing an install.sh file

It would also be nice to see an uninstall.sh file to simplify removal.

EDIT:
As rm is linked to rmw, you need rmw to cater for all the options that rm can use, especially rm -rf

Thanks yet again.

I issued another release, and included the install-sh file which 'configure' requires.
I also edited it, and the configure script to change #!/bin/sh to #!/bin/bash

I added support for the -rf option. It'll be a while before I add support for all the other other options 'rm' has. I'm recovering from some pain issues which limits the amount of time I'm able to spend at the computer. Know of any good speech recog programs? :)

As for an uninstall.sh, I don't know how to do that while accounting for what users might set for a prefix install dir using 'configure'. The install-sh file is just copied from my automake directory when I create the Makefile.in.

For a workaround, I added more information to the INSTALL file about where files are being copied to. I also added an UNINSTALL file with more instructions. Additionally, I created a .deb package and an .rpm package while logged into my Kubuntu 9.04 partition and those two files are available for download on the sourceforge project page. I used checkinstall to create the packages.

Thanks for all your suggestions.

rainbowbird 06-22-2009 03:27 AM

a little modification
 
Quote:

Originally Posted by Disillusionist (Post 3331020)
Code:

source="./tmp/myfile"
dest=~/.Waste/$(echo $source|sed "s@^./@$PWD@"|sed "s@^/@@")


Disillusionist:
a little modifiation :)
Code:

source="./tmp/myfile"
dest=~/.Waste/$(echo $source|sed "s@^./@$PWD/@"|sed "s@^/@@")


kj6loh 09-07-2009 02:34 PM

Quote:

Originally Posted by Andy Alkaline (Post 3327762)
I'd like to be able to move the file into ~/.Waste/original/path

Using $PWD doesn't guarantee that. I need to have the absolute path of the file returned.

(a few minutes later)
Found it.
Bash equivalent for PHP realpath()

Unfortunately readlink is not on every bash system

edvalson 11-02-2009 06:55 PM

Quote:

What is your concern with using $PWD? If concerned about someone exporting variables to an incorrect value, you could always use $(pwd) to set the value of PWD.
Because if you write scripts using absolute paths that are not depending on a) where the script starts from, or b) from cd'ing somewhere inside the script, $PWD is irrelevant.

ta0kira has it right: to return the absolute path of a filename that is originally given with a relative path:

for example you are at a bash prompt in /dir1/dir2/dir3/dir4

and the file is /dir1/dir2/file1

ABSOLUTE_DIR=$(dirname $(readlink -f ../../dir2/file1))

kwutchak 08-25-2010 05:16 AM

A solution I found elsewhere...
 
echo "Absolute path: `cd $1; pwd`"

http://peasleer.wordpress.com/2007/0...-path-in-bash/

catkin 08-25-2010 10:50 AM

Another solution is to use the external pwd command instead of the bash builtin. In the following illustration /home/c/Templates is a symlink to /home/c/d/Templates
Code:

c@CW9:~$ ls -l Templates
lrwxrwxrwx 1 c users 11 2010-06-07 11:43 Templates -> d/Template
c@CW9:~$ cd Templates
c@CW9:~/Templates$ pwd
/home/c/Templates
c@CW9:~/Templates$ echo $PWD
/home/c/Templates
c@CW9:~/Templates$ /bin/pwd
/home/c/d/Templates


Andy Alt 12-28-2010 02:58 PM

I didn't know readlink was nonstandard. Thanks to the suggestions, this is what I've come up with.

Code:

#!/bin/bash
# abspath.sh
# Find absolute path of file or symbolic link without using readlink
# December 28, 2010

function absp() {
  local FILE=$1
  local CWD=$(pwd)
 
  # remove trailing slash
  FILE=${FILE%/}

  local FBN=`basename "$FILE"`

  # Remove basename to get relative path

  local RP="${FILE%$FBN}"
 
  # change dir to relative path to run pwd
  cd "$RP"

  # Assign present working dir to $MOVETO; pwd returns the
  # absolute path
  AP=$(pwd)
       
  # Append filename to abs path
  APFN=$(pwd)/$FBN

  # Change back to directory we started in
  cd "$CWD"
 
#  echo -e "\n\tAbsolute path:"
#  echo -e "\t$AP"
#  echo -e "\n\tAbsolute path to file:"
#  echo -e "\t$APFN\n"
 
  return 0
}

absp $1
echo -e "\n\tAbsolute path:"
echo -e "\t$AP"
echo -e "\n\tAbsolute path to file:"
echo -e "\t$APFN\n"
exit 0


Andy Alt 12-28-2010 03:53 PM

I like this better:

Code:

#!/bin/bash
# abspath.sh 0.01
# Find absolute path of file or symbolic link without using readlink
# December 28, 2010

function absp() {

# Call this function by
# VAR=`absp <filename> ap|apfn>` # backticks required
# ap will return absolute path; apfn will return absolute path with filename

  local FILE=$1
  local CWD=$(pwd)
 
  # remove trailing slash
  FILE=${FILE%/}

  local FBN=`basename "$FILE"`

  # Remove basename to get relative path

  local RP="${FILE%$FBN}"
 
  # change dir to relative path to run pwd
  cd "$RP"

  # Assign present working dir to $MOVETO; pwd returns the
  # absolute path
  local AP=$(pwd)
       
  # Append filename to abs path
  local APFN=$(pwd)/$FBN

  # Change back to directory we started in
  cd "$CWD"
 
  case $2 in
  ap)
  echo "$AP"
  ;;
  apfn)
  echo "$APFN"
  ;;
  *)
  echo "NULL";return 1;break
  ;;
  esac
 
  return 0
}

# absp function takes two parameters; file name and ap|apfn
# This method allows different variable names for AP or APFN throughout the program

ABSOLUTE_PATH=`absp $1 ap`
ABSOLUTE_FILE=`absp $1 apfn`

echo $ABSOLUTE_PATH
echo $ABSOLUTE_FILE

exit 0


Andy Alt 01-24-2011 02:50 AM

I made another change. In the previous function I posted, it has to be called three times if the absolute path, absolute path with filename, and base filename are required. That can slow things down. This method may be slightly more memory intensive, but speeds up the process.

Code:

function gfp_info() {

  local FILE=$1
 
  # save pwd to cd back to it below
  local CWD=$(pwd)
 
  # remove trailing slash
  FILE=${FILE%/}

  # Get the basename of the file
  local file_basename=`basename "$FILE"`

  # Remove basename to get relative path
  # Using bash's string function instead of calling
  # dirname
  local RP="${FILE%$file_basename}"

  # change dir to relative path
  cd "$RP"

  # Assign present working dir to $fileap; pwd returns the
  # absolute path
  local fileap=$(pwd)
       
  # Append base filename to absolute path
  local ap_with_basename=$(pwd)/$file_basename

  # Change back to directory we started in
  cd "$CWD"
 
  AP="${fileap}"
  APFN="${ap_with_basename}"
  FBN="${file_basename}"

  return 0

}

# Initialize variables used in function so they can be used globally
AP=""
APFN=""
FBN=""

gfp_info "$FILENAME"

# The three variables above will now have value.


catkin 01-24-2011 10:50 AM

It could go a little faster if:
  1. local CWD=$(pwd) was removed and cd "$CWD" was changed to cd -
  2. file_basename=`basename "$FILE"` was changed to file_basename="${FILE##*/}"
  3. fileap=$(pwd) was changed to fileap=$PWD
  4. ap_with_basename=$(pwd)/$file_basename was changed to ap_with_basename=$fileap/$file_basename or ap_with_basename=$PWD/$file_basename. In the latter case $fileap need not be used if suggestion 6 is implemented.
  5. AP=""
    APFN=""
    FBN=""
    were removed; these variables are created as global variables by assigning values to them in the function.
  6. AP="${fileap}" were moved prior to cd "$CWD" in which case $fileap need not be used if suggestion 4, second option is implemented.
Incidentally ...

pwd does not return the absolute path if the path was reached via a symlink in which case pwd -P is needed:
Code:

c@CW8:~$ cd /tmp
c@CW8:/tmp$ mkdir foo
c@CW8:/tmp$ ln -s foo bar
c@CW8:/tmp$ cd bar
c@CW8:/tmp/bar$ pwd
/tmp/bar
c@CW8:/tmp/bar$ pwd -P
/tmp/foo

RP="${FILE%$file_basename}" does not generate a relative path; it extracts the directory component of the full path. A relative path is one that does not begin with "/". The same could be achieved by the common idiom RP=${FILE%/*} (the double quotes are not necessary on the RHS of an assignment "=". Strictly speaking these are not "bash string functions" but bash parameter expansions.

catkin 01-24-2011 10:51 AM

Quote:

Originally Posted by catkin (Post 4077325)
Another solution is to use the external pwd command instead of the bash builtin.

That works but using the pwd builtin with the -P option is faster.

Andy Alt 01-25-2011 03:39 PM

I've made the changes to my script, with some exceptions.

cd "-" &>/dev/null

And when I was using rmw on a file in my present working directory, RP=${FILE%/*} would kick out an error. (No slash to find)

Thanks for the suggestions, catkin. Good stuff.

catkin 01-26-2011 03:28 AM

Quote:

Originally Posted by Andy Alkaline (Post 4237563)
And when I was using rmw on a file in my present working directory, RP=${FILE%/*} would kick out an error. (No slash to find)

Strange; it should silently do nothing as in
Code:

c@CW8:/tmp$ FILE=foo
c@CW8:/tmp$ var=${FILE%/*}
c@CW8:/tmp$ echo $var
foo


Andy Alt 01-26-2011 01:20 PM

I was wrong about the slash, but the code you suggested assigns the basename, whereas I want everything except for the basename. Since I'm not good at explaining, allow me to illustrate:

Code:

  # extracts the directory component of the full path
#  local DC="${FILE%$file_basename}"
  local DC="${FILE%/*}"
  echo -e $c_1"FILE: $FILE"$c_reset
  cd "$DC"
  echo -e $c_1"DC: \"$DC\" pwd: - \"$(pwd -P)\" "$c_reset
  # Assign present working dir to $fileap; pwd returns the
  # absolute path
  local fileap=$(pwd -P)

andy@pigeon:~/Documents$ rmw temp
FILE: temp
/home/andy/bin/rmw: line 451: cd: temp: Not a directory
DC: "temp" pwd: - "/home/andy/Documents"
`temp' -> `/home/andy/.Waste/files/temp.2011-01-26h13m11s43'

---

Code:

  # extracts the directory component of the full path
  local DC="${FILE%$file_basename}"
#  local DC="${FILE%/*}"
  echo -e $c_1"FILE: $FILE"$c_reset
  cd "$DC"
  echo -e $c_1"DC: \"$DC\" pwd: - \"$(pwd -P)\" "$c_reset
  # Assign present working dir to $fileap; pwd returns the
  # absolute path
  local fileap=$(pwd -P)

andy@pigeon:~/Documents$ rmw temp
FILE: temp
DC: "" pwd: - "/home/andy/Documents"
`temp' -> `/home/andy/.Waste/files/temp.2011-01-26h13m14s23'

I've added an if clause around cd $DC
Code:

if [ $DC ]; then 
    cd "$DC"
  fi

If in the same dir as the file, no need to tell cd to change to nothing and waste everyone's time.

catkin 01-26-2011 11:41 PM

For the basename: basename=${fullpath##*/}. It looks like this at the command prompt:
Code:

c@CW8:/tmp$ fullpath=/etc/rsyncd.conf
c@CW8:/tmp$ basename=${fullpath##*/}
c@CW8:/tmp$ echo $basename
rsyncd.conf


Andy Alt 01-27-2011 09:37 AM

Code:

function gfp_info() {

# Thanks to catkin for the massive improvements to this function
# http://www.linuxquestions.org/questions/user/catkin-444761/

  local FILE=$1
 
  # remove trailing slash
  FILE=${FILE%/}

  # Get the basename of the file
  local file_basename="${FILE##*/}"

  # extracts the directory component of the full path
  local DC="${FILE%$file_basename}"

  if [ $DC ]; then 
    cd "$DC"
  fi

  # Assign present working dir to $fileap; pwd returns the
  # absolute path
  local fileap=$(pwd -P)
       
  # Append base filename to absolute path
  local ap_with_basename=$fileap/$file_basename

  # Change back to directory we started in
  cd "-" &>/dev/null
 
  AP="${fileap}"
  APFN="${ap_with_basename}"
  FBN="${file_basename}"

  return 0

}


arifsaha 03-31-2011 11:16 AM

Yes even another code for absolute path
 
How about this one to get absolute path? Simpler, even local variables are not needed. Little modification will get you the file base name if needed, as soon as you can define its behaviour when you give directory as argument (should it be the last directory or empty string?).

Code:

function abspath {
        if [[ -d "$1" ]]
        then
                pushd "$1" >/dev/null
                pwd
                popd >/dev/null
        elif [[ -e $1 ]]
        then
                pushd $(dirname $1) >/dev/null
                echo $(pwd)/$(basename $1)
                popd >/dev/null
        else
                echo $1 does not exist! >&2
                return 127
        fi
}


konsolebox 04-09-2011 04:19 AM

you can try mine as well:
Code:

function getabspath {
        local -a T1 T2
        local -i I=0
        local IFS=/ A

        case "$1" in
        /*)
                read -r -a T1 <<< "$1"
                ;;
        *)
                read -r -a T1 <<< "/$PWD/$1"
                ;;
        esac

        T2=()

        for A in "${T1[@]}"; do
                case "$A" in
                ..)
                        [[ I -ne 0 ]] && unset T2\[--I\]
                        continue
                        ;;
                .|'')
                        continue
                        ;;
                esac

                T2[I++]=$A
        done

        case "$1" in
        */)
                [[ I -ne 0 ]] && __="/${T2[*]}/" || __=/
                ;;
        *)
                [[ I -ne 0 ]] && __="/${T2[*]}" || __=/.
                ;;
        esac
}

Code:

getabspath "/path/.././././to/somewhere/"
echo "$__"

getabspath ".././././....//path/.././././to/somewhere/with/somefile.ext"
echo "$__"

note that the path or files does not need to exist.

the function can also be modified to accept another argument which could represent the name of the variable which will hold the result. i.e. like
Code:

getabspath <path> <variable_name>

Andy Alt 07-07-2011 05:20 PM

Calling a binary from a bash script can sometimes be preferred. Here's some C code which can be compiled using gcc -Wall -o getpath getpath.c

Code:

//      getpath.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{       
        if (argc != 2)
                {
                        printf("Getpath takes one filename or directory as a parameter\n");
                        exit (1);
                }
       
        char AbsPath[strlen(argv[1] + 1)];
        realpath(argv[1],AbsPath);
        printf("%s\n",AbsPath);
        return 0;
}

So if desired, copy the code above to a plain text file called getpath.c and compile it. Then use "./getpath <filename>" If you put getpath is in your path somewhere, the ./ can be omitted.

fatso83 06-19-2012 03:27 AM

Thanks for the script!
 
The script from arifsaha was without doubt leaps and bounds better than any of the others; clean, simple and cross-platform compatible. Works just as well on BSD as on GNU based toolsets. I will try to give credit whenever I use it - which I suspect will be plenty more than the three applications so far :-)

Quote:

Originally Posted by arifsaha (Post 4309783)
How about this one to get absolute path? Simpler, even local variables are not needed.
Code:

function abspath {
        if [[ -d "$1" ]]
        then
                pushd "$1" >/dev/null
                pwd
                popd >/dev/null
        elif [[ -e $1 ]]
        then
                pushd $(dirname $1) >/dev/null
                echo $(pwd)/$(basename $1)
                popd >/dev/null
        else
                echo $1 does not exist! >&2
                return 127
        fi
}



pan64 06-19-2012 03:34 AM

just one comment: instead basename and dirname you can use parameter substitution:
Code:

${var##*/}  # <== basename
${var%/*}  # <== dirname








_____________________________________
If someone helps you, or you approve of what's posted, click the "Add to Reputation" button, on the left of the post.
Happy with solution ... mark as SOLVED
(located in the "thread tools")

fatso83 06-19-2012 10:01 AM

Readability
 
I mentioned clean and simple; for the uninitiated, that looks like voodo :study: I prefer verbosity over terseness if the terseness comes with a reduction in readability.

pan64 06-19-2012 11:11 AM

actually it is not really so simple: using parameter substitution will be processed inside the shell, using basename and dirname will fork another task, will open pipe therefore it costs more, much more. If you were familiar with bash enough you should not speak about readability problems in such cases....

fatso83 06-19-2012 11:41 AM

Thanks for the info
 
Quote:

Originally Posted by pan64 (Post 4707018)
If you were familiar with bash enough you should not speak about readability problems in such cases...

I have never claimed to be a bash expert, and for the case of the original script, one did not need to be either, which is why I liked it :) For most people hacking together small scripts to get some job done it does not pay to invest time into learning the ins and outs of the various shell, which is why shell scripts should be mostly self-explanatory. Or just heavily commented ;) But the info on why parameter substitution might prove a better fit was very interesting (for me, at least), and put the matter into another perspective. Thanks for the info - nice to know!

konsolebox 06-26-2012 07:05 PM

Quote:

Originally Posted by fatso83 (Post 4706747)
The script from arifsaha was without doubt leaps and bounds better than any of the others; clean, simple and cross-platform compatible. Works just as well on BSD as on GNU based toolsets. I will try to give credit whenever I use it - which I suspect will be plenty more than the three applications so far :-)

Unfortunately the directories need to exist in order for that to work. Also, you'll have to use external commands which is rather slower and require dependencies.

konsolebox 06-26-2012 07:09 PM

Check out my full post about getting absolute pathnames in all general shells and also for a binary format.

http://www.linuxquestions.org/questi...-scripts-3956/

Andy Alt 07-06-2012 12:10 AM

The realpath command was added to coreutils, as of version 8.15, I believe. Not the best solution yet -- not all *nix distros have it installed yet, and I couldn't say anything about Macs.

See also: GNU core utilities

fatso83 07-06-2012 10:24 AM

Quote:

Originally Posted by Andy Alkaline (Post 4720517)
The realpath command was added to coreutils, as of version 8.15, I believe. Not the best solution yet -- not all *nix distros have it installed yet, and I couldn't say anything about Macs.

See also: GNU core utilities

Seems good - albeit it does not exist on Mac or on any of our servers (typically 3-5 years behind). So a cross-platform script comes in handy :)

fatso83 01-10-2013 06:21 AM

Parameter substitution not working?
 
I just tried the suggestions from pan64, but I was unable to make it work. Can anyone change the script above and make it work with parameter substitution? I have also searched the man page for bash on the "var##" pattern, and I cannot find anything referencing it.

Quote:

Originally Posted by pan64 (Post 4706751)
just one comment: instead basename and dirname you can use parameter substitution:
Code:

${var##*/}  # <== basename
${var%/*}  # <== dirname








_____________________________________
If someone helps you, or you approve of what's posted, click the "Add to Reputation" button, on the left of the post.
Happy with solution ... mark as SOLVED
(located in the "thread tools")


pan64 01-10-2013 06:39 AM

please open a new thread and specify your problem, or at least show us what you tried:
Code:

$ var=/a/v/b/n/f/e
$ echo ${var##*/}
e
$ echo ${var%/*}
/a/v/b/n/f
$


konsolebox 01-10-2013 09:11 AM

I wonder how my script could not provide the one you require that you have to go all the troubles.


All times are GMT -5. The time now is 05:08 PM.