1

I have a bash script called findme, which is coded as follows:

#!/bin/bash
locate -Abi '*\.'$1 $2 | grep --color=always -ni $2 | less -R

It searches for me for all files with a specified file extension (first argument provided to the script) which has a pattern (the next provided argument) in its filename.
If I execute the following:

user@machine$ findme pdf classifi

It will search for all pdf files with classifi in its filename. So I may get something like the following result.

1:/home/user/Dropbox/SharedWithFriends/math/classifications2000.pdf
2:/home/user/Dropbox/SharedWithFriends/math/classifications2010.pdf

The question is: "Can you give me a bash script code in which after showing the results ask me for a number and a viewer, to automate my next job?"

for example I would like, if I enter:

> 2 evince

the script executes evince on the 2nd item of the search result, i.e., if this is what I entered on the previous search result it executes:

evince /home/user/Dropbox/SharedWithFriends/math/classifications2010.pdf
  • do you imagine changing findme to have a prompt at the end for you to enter the item # and viewer, or would you leave findme unchanged and instead have a separate, second function openme that accepted 2 evince as parameters? – Jeff Schaller Nov 2 '16 at 19:31
2

Here's a variation on your findme function that pulls up the results, but instead of using grep to number them, or less to page them, it lists them from an internal array, then prompts for your item and program choice.

#!/usr/bin/env bash

readarray -O 1 -t results < <(locate -Abi '*\.'"$1" "$2" | grep --color=always -i "$2")

for((i=1; i <= ${#results[*]}; i++))
do
  printf "%d: %s\n" $i "${results[i]}"
done
read -p "> " item program
$program "${results[item]}"

I added some quoting to your original script to better deal with spaces in filenames, or even grep parameters. I adjusted the readarray call so that the results start at index 1 instead of 0, so that it corresponds to grep's numbering.

  • I is great! It is almost what I want! Except it gives me an error... please keep in mind that the results have escape codes! can you fix if for me!? – texinfo Nov 2 '16 at 22:46
  • Numbering from 0 is also fine! – texinfo Nov 2 '16 at 22:46
  • I need something to page the results sometime the search result returns me 70 items! – texinfo Nov 2 '16 at 22:48
2

Note that your findme script has a few problems:

  • Missing double quotes around variable substitutions
  • The output of grep --color=always produces results that can't be used in a command substitution. You need it to pass through less, but don't try to reuse it in a script.
  • grep and locate use different pattern syntax, so using grep to color the second argument won't always work. Passing -r to locate makes it use regexps, but with Emacs syntax which is slightly different from the syntaxes that grep supports.

In bash, you can use mapfile to reliably stuff some lines into an array. Combine it with process substitution to use the output of a command. Then print that array and read the user's input.

findrun () {
  mapfile search_hits <(locate -Abir ".*\.$1" "$2")
  print '%s\n' "${search_hits[@]}" | grep --color=always -ine "$2"
  if read -a cmd; then
    set -- "${cmd[@]}"
    set -- "$@" "${search_hits[$1]}"
    shift
    "$@"
  fi
}

An alternative interface would be to set the positional parameters. It's a bit tricky, because you can't change the positional parameters from a function, but there's a roundabout way to do it in bash by using an alias and sourcing a script. Beware of quoting.

alias findrun='. <(echo findrun_prepare \"\$@\"; echo set -- "\"\${search_hits[@]}\"")'
findrun_prepare () {
  mapfile search_hits <(locate -Abir ".*\.$1" "$2")
  print '%s\n' "${search_hits[@]}" | grep --color=always -ine "$2" >&2
}

Usage:

findrun pdf classifi
evince "$2"
  • Your first code gives me the following error (when I try to run findrun pdf classifi): locate: non-option arguments are not allowed with --regexp – texinfo Nov 3 '16 at 12:03
  • @texinfo Oh, damn, I hadn't tested that. I think my answer would work without -r, as long as you accept the inconsistency which won't matter if you only look for strings (e.g. findrun pdf classifi as opposed to findrun pdf cl*ifi). – Gilles 'SO- stop being evil' Nov 3 '16 at 13:47
0

Based on the Solution given by Jeff Schaller. I found a solution, I would be glad if I receive your comments, to make it better.

#!/bin/bash

readarray -O 1 -t results < <(locate -Abi '*\.'"$1" "$2")

for((i=1; i <= ${#results[*]}; i++))
do
  printf "%d: %s" $i "${results[i]}" | grep --color=always -i "$2"
done

read -p "> " item program

$program "${results[item]}"

This looks like a simple (up to complexity of the topics) answer.

But is not fine enough if the results be more than a certain number! In that case we need something like more or less

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.