Rodrigo Gurgel
Rodrigo Gurgel

Reputation: 1736

egrep results to vim as a line referenced filelist

In a shell I use the following function to create a filelist and pass it to vim.

Feels alright, but I lost the line reference, I open the files in correct order but then I have to search again for the text as the cursor starts at first line.

Actual function on ~/.bashrc

function vimgrep(){
   vim `grep -IR "$1" * | awk -F: '$1!=f{print ""$1"";f=$1}' | grep -v 'Test\|test'` +ls
}

function vimgrep2(){
   vim `grep -IR "$1" * | awk -F: '$1!=f{print ""$1"";f=$1}' ` +ls
}

Obs.: filelist must come from shell to vim, and then it must keep the line reference of the buffered files, just like with the results of :make when it catches any error (but without the bottom window [:cwindow]).

edited: Ok... not so elegant, but I could pass the searched string to vim as +/"$1", like:

   vim `grep -IR "$1" * | awk -F: '$1!=f{print ""$1"";f=$1}' ` +/"$1"

Would be better if the script doesn't use a temporary file.

Upvotes: 5

Views: 617

Answers (5)

Swiss
Swiss

Reputation: 5839

Here are some functions that provide you with exactly what you want. It will open each file in a buffer with the line set to the first line that matches your given regex.

I tend to prefer short descriptive bash functions like this, since bash is hard enough to understand without using massive blocks of code.

The only flaw with this is that it doesn't filter out binary files effectively. An update to the find_nonbinary function will fix this.

These are helper functions. They are not to be run directly.

# Using print0 to handle files with weird names.
# This is just a quick placeholder.
# You can play with this to get better results.
function __find_nonbinary() {
    find -type f -print0
}

function __generate_vim_cmds() {
    xargs -0 awk -v regex="$regex" \
        '$0 ~ regex {printf "e +%s %s\n", FNR, FILENAME; nextfile}'
}

function __combine_vim_cmds() {
    declare -a vim_cmds

    while read line; do
        vim_cmds+=( " $line " )
    done

    # Force subshell to prevent IFS from being clobbered.
    (
        IFS="|"
        echo "${vim_cmds[*]}"
    )
}

This is the actual function to use.

function vimgrep() {
    local regex="$1"

    if [[ -z "$regex" ]]; then
        echo "Usage: $0 regex" 1>&2
        exit 1
    fi

    local vim_cmds=$(
        __find_nonbinary    |
        __generate_vim_cmds |
        __combine_vim_cmds
    )

    if [[ -z "$vim_cmds" ]]; then
        echo "No files matching $regex found." 1>&2
        exit 1
    fi

    vim -c "$vim_cmds"
}

Upvotes: 1

Swiss
Swiss

Reputation: 5839

Vim also comes with a vimgrep command you could use

function vimgrep() {
    local regex="$1"

    if [[ -z "$regex" ]]; then
        echo "Usage: $0 regex" 1>&2
        exit 1
    fi

    vim -c "vimgrep /$regex/ **"
}

Be careful of running it in a directory with a lot of files below it.

Upvotes: 1

suvayu
suvayu

Reputation: 4664

Disclaimer: I know nothing about vim

The man page says with an argument like +<line no> it will jump to that line for the provided file. Based on this information I whipped up this script. It relies on a command line like the following to start vim:

$ vim +n1 file1 +n2 file2

You could also put it as a shell function in your '~/.bashrc as it is quite small.

Usage: vimgrep -e <search pattern> <fileglob>

The search pattern can be any regular expression understood by grep, and file glob is a shell glob understood by bash.

#!/bin/bash

# set -o xtrace # uncomment to debug

# declare variables
declare -a grepopts files

[[ $1 =~ -e$ ]] && grepopts+=("-e") && grepopts+=("$2") && shift 2

files=("${@}")

vim $(egrep -n -m1 -H "${grepopts[@]}" "${files[@]}" | \
        sed -e 's/^\(.\+\):\([0-9]\+\):.\+$/+\2 \1/')

Caveat: This script will fail if the file name has a : character in it.

Upvotes: 1

perreal
perreal

Reputation: 98118

With this you can move between matches via cn, cp, cc.. ect:

function vimgrep(){
   vim -c "exec(\"grep $1 *\")"
}

With highlighting:

function vimgrep(){
   vim -c "exec(\"grep -IR $1 *\")" -c "let @/='$1'" -c "set hls"
}

Upvotes: 1

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84453

Using Line Numbers with an External Grep

To the best of my knowledge, it is not possible to open more than one file at a time using the line number option flag because Vim will only apply the flag to the first file in the list. The man page says:

 +[num]      For the first file the cursor will be positioned on line  
             "num". If "num" is missing, the cursor will be positioned
             on the last line.

So, you could call the function on each file one at a time, or put the function call in a loop that operates on two positional parameters (a file name and a regular expression) at a time. The former is certainly easier. For example:

vimgrep () {
    local file="$1"
    shift || return 1
    vim +$(egrep -n "$1" "$file" | cut -d: -f1) "$file"
}

# Sample function call.
vimgrep /etc/password '^www-data'

Upvotes: 1

Related Questions