5

What's the bash equivalent to os.path.normpath? Specifically I'm interested in removing the leading ./ given when executing find.

matt@stanley:~/src/libtelnet-0.20/test$ find
.
./Makefile
./Makefile.in
./Makefile.am
...
3
  • 4
    Out of curiosity, why? ./foo works fine, so programs shouldn't care... Commented Jun 18, 2011 at 1:41
  • ./Makefile is not an absolute pathname, so you can use it without any issues. There. Problem solved. Commented Jun 19, 2011 at 19:59
  • @Nemo: The normalized paths are used in a manifest file. Otherwise it wouldn't be a problem at all. Commented Jun 20, 2011 at 4:15

7 Answers 7

2

Well, for that, you can simply pipe the output through sed, you don't have to normalise the entire path:

your_command_goes_here | sed 's?^\./??'

That will get rid of all ./ sequences at the start of a line.

The following transcript shows this in action:

pax$ find -name 'qq*sh'
./qq.ksh
./qq.sh
./qq.zsh
./qq2.sh
./qq2.zsh
./qqq/qq.sh

pax$ find -name 'qq*sh' | sed 's?^./??'
qq.ksh
qq.sh
qq.zsh
qq2.sh
qq2.zsh
qqq/qq.sh

As you can see, I have a fairly intuitive naming standard for my temporary shell scripts :-)

Sign up to request clarification or add additional context in comments.

Comments

2

I'm struggling to find a case where I'd want to use os.path.normpath. On a system that has symbolic links, such as unix or Windows, the value that it returns may not designate the same file:

$ mkdir /tmp/one /tmp/one/two
$ ln -s /tmp/one/two /tmp/foo
$ python -c 'import os.path; print os.path.normpath("/tmp/foo/..")'
/tmp
$ ls /tmp/foo/..
two

/tmp/foo/.. is /tmp/one, not /tmp!

On Linux, readlink -- "$filename" normalizes all symbolic links in a path. The file name it returns designates the same file as $filename at the time the command is executed (it might not, later, if one of the symlinks involved is changed). But most of the time, that's not necessary: just keep $filename as it is.

If you want to remove a ./ prefix for cosmetic reasons, just strip it specifically.

filename=${filename#./}
find | sed -e 's!^\./!!'

1 Comment

The normalized paths are used in a manifest file. They cannot be absolute.
2

I found it: it's called realpath! Type something like:

realpath ..   # the most interesting thing

or

realpath .    # equivalent to `pwd' and to `echo $PWD'

and enjoy!

Unlike os.path.normpath, realpath requires the path to exist. If you just want to normalise it as a string realpath -m can be used.

1 Comment

Would have been nice, but it's not standard.
1

I usually do this by using find's -printf argument.

The following works fine if you're searching in multiple paths:

find path1 path2

The following works fine if you're searching in .:

find -printf '%P\n'

If you have a mixed paths (e.g. find path1 path2 .), you'd have to use sed.

1 Comment

I'm not sure what you mean by mixed paths it seems to work find without it.
1

How about:

newpath=`echo -n "$oldpath" | python -c 'import sys, os; print os.path.normpath(sys.stdin.readline())'`

?

I do not think there is any built-in bash function to do everything Python's normpath does. You might be better off describing exactly what transformation you want to perform.

1 Comment

Pity print switches behaviour between 2 and 3 :)
1

Write something like this:

normpath() {
    [[ -z "$1" ]] && return

    local skip=0 p o c

    p="$1"
    # check if we have absolute path and if not make it absolute
    [[ "${p:0:1}" != "/" ]] && p="$PWD/$p"

    o=""
    # loop on processing all path elements
    while [[ "$p" != "/" ]]; do
        # retrive current path element
        c="$(basename "$p")"
        # shink our path on one(current) element
        p="$(dirname "$p")"

        # basename/dirname correct handle multimple "/" chars
        # so we can not warry about them

        # skip elements "/./" 
        [[ "$c" == "." ]] && continue
        if [[ "$c" == ".." ]]; then
            # if we have point on parent dir, we must skip next element
            # in other words "a/abc/../c" must become "a/c"
            let "skip += 1"
        elif [[ 0 -lt $skip ]]; then
            # skip current element and decrease skip counter
            let "skip -= 1"
        else
            # this is normal element and we must add it to result
            [[ -n "$o" ]] && o="/$o"
            o="$c$o"
        fi
    done

    # last thing - restore original absolute path sign
    echo "/$o"
}

1 Comment

I have added some comments. To make not absolute path "cleaning" we should change loop condition and add handle to situation when path go above it's starting point(add required count "../" elements)
0

Possible duplicate here. But if you are just interested in striping off leading ./ you could just do

find -type f | sed 's/^\.\///'

1 Comment

@Matt, funny, I had escaped it. But chosen some formatting > which did not show it properly. I choose pre formatting now and it shows

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.