13

Query

I use Bash. When I'm looking for files, often I'll do the following:

find -name stackexchange.hs

And often the results will look like:

/youre/the/man/now/dog/stackexchange.hs
/you/are/no/longer/the/dog/dog/stackexchange.hs
/this/is/the/file/i/want/stackexchange.hs

Then I'll want to do one of the following:

  • Option 1: Open the last item in the list of results in vim.
  • Option 2: Open the Nth item in the list of results in vim.

Currently, I cut-and-paste with the mouse. Which brings me to my question:

  1. Is there an easy, one-liner to accomplish options 1&2? Note that this is occurring after the find command.
  2. Is there a way to capture N-lines from stdout in some kind of bash vector/array?

Ideal usage

$ find -name am_i_really_all_alone.txt
./borges/library/you_are_not_alone.txt
./borges/library/am_i_really_all_alone.txt
$ vim (N)

(syntax and semantics may differ, but you get the point)

Similaria

There seem to be several similar questions. Here are my perceived differences (I am open to enlightenment):

Thank you for your help! Having used *nix/BSD when I was a teenager in the 90s and getting scared away by calling my burnout, acid-head neighbour to help me install drivers for my plug-and-play sound card, I'm relieved to discuss command-line minutiae with (perceivably) less frightening individuals. It feels good to be back.

  • I think, that if you know it before that you want to open the last result, you could use something like vim $(command |tail -n1). – varesa Jan 03 '13 at 21:26
  • I posted a similar question here http://unix.stackexchange.com/questions/348224/quick-way-to-open-results-from-find-or-locate – joelostblom Mar 03 '17 at 04:58

4 Answers4

10

Here is a potential solution to your issue that should be reasonably (but not perfectly) safe in the presence of funky file names (doesn't handle filenames with linefeeds in them - probably fixable, but there might be other problems lurking).

Two functions, the first one runs find with the parameters you pass it, saves the output in an array, and displays them. The second is just a helper to access that array.

myfind() {
  IFS=$'\n' __last_find_result=($(find "$@"));
  printf "%s\n" "${__last_find_result[@]}";
}
myget() {
  echo "${__last_find_result[$1]}";
}

Use case:

$ myfind . -name "c*"
./a b/c d
./.git/config
./.git/hooks/commit-msg.sample
$ vim "$(myget 0)"
# This opens the "./a b/c d" file.
$ vim "$(myget 2)"
# This opens ".git/hooks/commit-msg.sample"

Quotes not required around the $(myget index) if you don't have whitespace or other troublesome characters in your filenames.
Pushes the whole output of find to your environment, which might be limited. (Using a temporary file rather than that array would solve that, but has other issues - notably concurrent use from multiple shells.)

Mat
  • 52,586
6

I've got this in my .screenrc:

bind -c pasteline 1 eval copy 'stuff "-Y"' 'paste .'
bind -c pasteline 2 eval copy 'stuff "2-Y"' 'paste .'
bind -c pasteline 3 eval copy 'stuff "3-Y"' 'paste .'
bind -c pasteline 4 eval copy 'stuff "4-Y"' 'paste .'
bind -c pasteline 5 eval copy 'stuff "5-Y"' 'paste .'
bind -c pasteline 6 eval copy 'stuff "6-Y"' 'paste .'
bind -c pasteline 7 eval copy 'stuff "7-Y"' 'paste .'
bind -c pasteline 8 eval copy 'stuff "8-Y"' 'paste .'
bind -c pasteline 9 eval copy 'stuff "9-Y"' 'paste .'
bindkey ¬ command -c pasteline

Basically, in screen, ¬1, pastes the line above the cursor, ¬2, pastes the second line above the cursor... and so on. You may want to add more for lines 10 and above, but I find that after about 7 already, I'd rather use the mouse or screen's copy mode than counting the number of lines to get the one I want.

0

Mats answer was just what I was looking for. I've expanded his code a bit to allow for more get-options.

$ f ~/scripts -name '*.sh'
$ vim $(g foo)  # edit all find results matching "foo"
$ vim $(g 1 3 5) # edit find results number 1, 3 and 5
$ vim $(g 3-5) # edit find results 3-5
$ vim $(g 5-) # edit find results 5 to last
$ vim $(g -7) # edit find result 7 from bottom
$ vim $(g 1 4-5 -7 9- foo) # all of the above combined

.

f() {
    IFS=$'\n' __last_find_result=($(find "$@"));
    printf "%s\n" "${__last_find_result[@]}";
}

g() {
    len=${#__last_find_result[@]}
    pad=${#len}
    numbers=""
    if [ "$1" == "-n" ]; then
        numbers=1
        shift
    fi
    if [ -z "$1" ]; then
        if [ -n "$numbers" ]; then
            n=1;
            for e in "${__last_find_result[@]}";do
                printf "%0${pad}d. %s\n" "$n" "$e"
                let n=n+1
            done
        else
            printf "%s\n" "${__last_find_result[@]}"
        fi
    else
        for l in $@;do
            if [[ "$l" =~ ([^0-9-]+) ]];then
                n=1;
                for e in "${__last_find_result[@]}";do
                    if [[ $e =~ $1 ]]; then
                        if [ -n "$numbers" ];then
                            printf "%0${pad}d. %s\n" "$n" "$e"
                        else
                            printf "%s\n" "$e"
                        fi
                    fi
                    let n=n+1
                done
            elif [[ "$l" =~ ^([0-9]+)$ ]];then
                let l=l-1
                echo "${__last_find_result[$l]}";
            elif [[ "$l" =~ ^([0-9]*)(-)?([0-9]*)$ ]]; then
                from=${BASH_REMATCH[1]};
                dash=${BASH_REMATCH[2]};
                to=${BASH_REMATCH[3]};
                if [ -z "$from" ]; then # -n
                    [ $to -gt ${#__last_find_result[@]} ] && to=${#__last_find_result[@]}
                    echo "${__last_find_result[-$to]}";
                else # n-m
                    [ -z "$to" ] && to=${#__last_find_result[@]}
                    [ $to -gt ${#__last_find_result[@]} ] && to=${#__last_find_result[@]}
                    let to=$to-1
                    let from=$from-1
                    n=$(($from+1))
                    for i in `seq $from $to`;do
                        if [ -n "$numbers" ];then
                            printf "%0${pad}d. %s\n" "$n" "${__last_find_result[$i]}"
                        else
                            printf "%s\n" "${__last_find_result[$i]}"
                        fi
                        let n=n+1
                    done
                fi
            fi
        done
    fi
}
bumby
  • 1
0

another solution is: you can write an interactive script that will automatically ask for your choice. here is the code for interactive script:

#!/bin/bash

echo "enter your choice : z for last argument or a number for that file"
read choice

case "$choice" in
z) eval vim \$$#;;
*)eval  vim \$$choice;;
esac

save this script with any name say "autofind" and invoke the script with your "find command" as argument here is the code to invoke script:

./autofind `your find command`

But before using the script check your "find command" whether it is giving result or not.If it is showing some result then only use the script