2

I used ls | agrep "<search pattern #1>;<search pattern #2>;...;<search pattern #n>" to quickly and/or effectively perform a search/list of some file(s) in a directory with some search pattern(s). As an illustration, I created an empty directory and filled it with some empty files using the touch command as shown below:

~ ls -l
total 0
-rw-r--r-- 1 me users 0 May  7 14:00 animals-bird_dog_cat.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-bird_dog.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-cat_dog_bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-cat_dog.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-cat.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog_bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog_cat_bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog_cat.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog.txt

Then, using ls -l | agrep "cat;bird", I have no problem to search/find file(s) whose name(s) contain only cat and bird as shown below.

~ ls -l | agrep 'cat;bird'
-rw-r--r-- 1 me users 0 May  7 14:00 animals-bird_dog_cat.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-cat_dog_bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog_cat_bird.txt

AFAICT, this method is pretty fast and effective (I am all ears for a better solution). Since I do this a lot on a daily base, I thought it may be better to write a simple shell script to perform such a task by simply giving the search pattern(s) in CLI. For instance, I wrote a simple shell scripts as shown below and named it mls and make it executable:

#!/bin/bash
bold=$(tput bold)
italic=$(tput sitm)
normal=$(tput sgr0)
if [ "$#" -lt 1 ]; then
    echo "${bold}Usage${normal}: $0 <search string 0> [... <search string n>]"
    exit 0
fi
patterns=""
for ((i=1;i<$#;i++))
do
    patterns+="${!i};"
done
patterns+="${!#}"
/usr/bin/ls -lart | agrep '${patterns}${!#}'

Then, when I executed the above shell script with a search pattern of mls cat bird, it returns nothing as shown below.

~ ls -l | agrep 'cat;bird'
-rw-r--r-- 1 me users 0 May  7 14:00 animals-bird_dog_cat.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-cat_dog_bird.txt
-rw-r--r-- 1 me users 0 May  7 14:00 animals-dog_cat_bird.txt
~ mls cat bird
~

Did I miss something?

berndbausch
  • 3,557
user91822
  • 121
  • The biggest problem is that you are parsing ls. See more here and what to do instead: https://unix.stackexchange.com/questions/128985/why-not-parse-ls-and-what-to-do-instead – Nasir Riley May 08 '21 at 13:59
  • Wow ... and you are right. Thank you and now the scripts work. – user91822 May 10 '21 at 17:17

2 Answers2

3

You could switch to zsh here whose globs support OR, AND (well AND-NOT and NOT) and approximation

$  set -o extendedglob # best in ~/.zshrc
$ ls -ld -- (#a1)*dig*~^(#a1)*bard*
-rw-rw-r-- 1 chazelas chazelas 0 May  8 16:24 animals-bird_dog_cat.txt
-rw-rw-r-- 1 chazelas chazelas 0 May  8 16:24 animals-bird_dog.txt
-rw-rw-r-- 1 chazelas chazelas 0 May  8 16:24 animals-cat_dog_bird.txt
-rw-rw-r-- 1 chazelas chazelas 0 May  8 16:24 animals-dog_bird.txt
-rw-rw-r-- 1 chazelas chazelas 0 May  8 16:24 animals-dog_cat_bird.txt
  • Thank you for your suggestion. I don't use zsh ATM; however, this has been added to my shell script arsenal. – user91822 May 10 '21 at 17:22
2

Aside from the single quote problem, you're adding the last element twice

patterns+="${!#}"
/usr/bin/ls -lart | agrep "${patterns}${!#}"

Run the program with bash -x mls cat bird to see


But you don't need a loop: the "$*" special parameter joins the positional parameters using the first character of IFS. If we temporarily define a new value for IFS, we can do

patterns=$( IFS=';'; echo "$*" )

If you don't need the "approximate" part of agrep, bash can do this by itself using extended glob patterns.

pattern=$( IFS='|'; echo "$*" )
shopt -s extglob
ls *@($pattern)*
glenn jackman
  • 85,964