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/)

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

}



All times are GMT -5. The time now is 06:30 AM.