Rugaard Rugaard - 8 days ago 6
Bash Question

Ascending recursion until file is found

First of all I'm very new to shell scripting. But I'm starting to love it more and more.

I really enjoy working on the Terminal, which why I started looking into customizing it for my needs by creating a personal prompt. You know, pro devs - pro prompts :-P

I'm pretty much satisfied with it at the moment. It has all the basic information that you would expect a prompt to have.

But I would love to add a non-standard "feature" to my prompt, but my newbieness is kicking my ass.

What I would love to do, is to create a method which looks for a specific file - in my case

composer.json
- in the current directory. If it's not found, it should jump up one level and look for it there. If it's not found there, it should jump up another level again and again and again until it finds the file.

At some point you'll probably hit the root
/
and then it should just abort.

The thing is, I want to be able to extract version numbers from a few specifically installed packages from the
composer.lock
file and show it in my prompt.

I don't seem to have a problem just looking for a file in the current directory, but it's the recursively ascending part that's problem.

I'm currently using the ZSH shell. Not sure if that provide some other features to achieve this than Bash.

Hope some of you incredible devs can help a shell fanboy out.

If I haven't made myself clear enough or if there is some information that you require that I haven't provided, don't hesitate to mention it and I'll do whatever it takes to give it.

Answer

I am kind of confused ... you say you are searching for composer.json but are talking about composer.lock ??? So below I will give you a script that finds composer.lock, you can change the file-variable at the beginning of the script if you want to change which file you're looking for!

I assumed your command composer show -i | grep "laravel/framework" would work in any directory the file composer.lock is found as you said in your comment. So if it has to be done without changing the current directory, there are 2 options:

  1. The cleanest way: push the current directory to the stack with pushd and before exiting pop the directory back with popd. Based on this website we shouldn't call pushd without argument

    One difference is the way pushd is handled if no arguments are given. As in csh, this exchanges the top two elements of the directory stack

    You could then try this:

    file=composer.lock
    unset failure
    # pushd actually current directory and change to current directory
    pushd .
    
    while [ ! -f "$file" ]
    do
        if [ "$(pwd)" != "/" ]
         then
            cd ..
        else
            failure=true
            break
        fi
    done
    
    if [ -z "$failure" ]
      then
        echo Found file in $(pwd) 
        # Your command!
        composer show -i | grep "laravel/framework"
        popd
    else
        echo File $file could not be found! >&2
        popd
        exit 1
    fi
    
    exit 0
    
  2. The other option is to hold a variable that holds the directory we've reached so far. This will allow to search for the file without changing directory at all!

    file=composer.lock
    unset failure
    cpath=$(pwd)
    
    while [ ! -f "$cpath/$file" ]
    do
        if [ "$cpath" != "/" ]
         then
            cpath=$(cd $cpath/.. && pwd)
        else
            failure=true
            break
        fi
    done
    
    if [ -z "$failure" ]
      then
        echo Found file in: $cpath
        # do something with $cpath ... 
    else
        echo File $file could not be found! >&2
        exit 1
    fi
    
    exit 0
    

It works in sh and bash. As I don't have a zsh installed, I couldn't try it out in zsh but I think it should work there too. I'm not sure though You can leave out the echo Found file in $(pwd) if you want to. I had it there for test purposes

Good luck

Comments