2

I have a shell script that runs a for loop to execute a command on multiple text files in a folder. I want to test if these files' names start with a specific set of letters (NA for instance), and have no special characters in the file (no periods except before a file extension, '-'s,'_'s, eg). I know how to test a file's full name, but I don't know how to test if part of it has those characters or how to specify the containing of only letters and numbers but no special characters except for the '.' in '.txt'. How would I go about doing this so that a command executes only on files meeting these requirements? I know it would use an if statement, but other than that I am not sure. It seems like situation to use either wildcards or reg-exs.

  • look into basename to strip off the extension; use echo $filename | grep ... for the rest – Jeff Schaller Jul 8 '15 at 19:08
  • how would that integrate into an if conditional? – cluemein Jul 8 '15 at 19:10
  • select files starting with 'NA'. – cluemein Jul 8 '15 at 19:28
3

The code below will allow you to perform some action on text files starting with "NA", only having alphanumeric characters, and ending in ".txt".

for f in /path/to/your/folder/*; do 
   if [[ ${f##*/} =~ NA[a-zA-Z0-9]*.txt ]]; then
      # perform some action on $f
   fi
done

If you want to allow your file to start with "N" or "A" use this:

for f in /path/to/your/folder/*; do 
    if [[ ${f##*/} =~ [N,A][a-zA-Z0-9]*.txt ]]; then
       # perform some action on $f
    fi
done
  • Can you explain this part: ${f##*/} =~ ? – Jodka Lemon Jul 8 '15 at 19:20
  • ${f##*/} is the same as $(basename $f) but without forking a subprocess. =~ allows for regular expressions. – airfishey Jul 8 '15 at 19:22
  • 2
    You need to remove the comma in [N,A] - because that allows for any filename which begins with an N or an A or a comma. Also, this only works properly in a POSIX locale, and, what's more it will not match any filenames containing any hyphens or underscores. – mikeserv Jul 8 '15 at 20:40
1

Here a fast hack:

#!/bin/bash
for i in *;
do
  if [ $(echo $i|cut -c 1-2) == "NA" ];
    then
    EXECUTE_COMMAND
  fi
done

Did I understand your question correct?

1

You could use extended globbing if your shell supports it, e.g.

bash:

shopt -s extglob
for f in  NA*([[:alnum:]]).txt; do something; done

zsh:

setopt extendedglob
for f in  NA([[:alnum:]])#.txt; do something; done

where *(...) and respectively (...)# mean zero or more occurrences.

  • You should do like: NA*([-_[:alnum:]]).txt, I think... – mikeserv Jul 8 '15 at 21:18
  • I read it the opposite - like only these kinds. It's really hard to tell, though. You're probably right. – mikeserv Jul 9 '15 at 2:02
1
for f in ./*
do    case ${f#??} in
      (*.*.*|*[!-_.[:alnum:]]*) ;;
      (NA*.txt)
          : do something w/ "$f"
      esac
done

The case statement allows you to branch code blocks for multiple possibilities. Though I make the above match for 2 or more dots or a not-dash-underscore-dot-alphanumeric character a no-op, you're just as free to put a code-block there as you are any other place. case matches as much as and as soon as it can, and so if you get to the NA*.txt match it is only because the filename which matches that pattern doesn't also match the pattern which came before.

And as I said, this is a branching statement, and so...

for f in ./*
do    case ${f#??} in
      (*.*.*|*[!-_.[:alnum:]]*) ;;
      (NA*.txt)
           : do something w/ "$f.txt";;
      (NA*.pdf)
           : do something w/ "$f.pdf";;
      (FA*.txt)
           : do something w/ "$f";;
      (*)
           : do something w/ everything else
      esac
done

...is perfectly valid. Each conditional block can be as you long as you could wish it - and newlines and the rest are valid within them. Every conditional block but the last must be delimited with two semicolons, but these are optional for the last conditional block.

As the case is evaluated each pattern is evaluated in turn. If a match is found, none of the following patterns are evaluated at all - they are effectively short-circuited in that case.

0

egrep should do:

egrep 'NA[A-Z a-z 0-9]*\..{3,4}$'

So ls /path/to/dir | egrep 'NA[A-Z a-z 0-9]*\..{3,4}$' would list:

NAfoo.txt
NAbar1.jpeg
NA123baz.txt

but would exclude:

aiNA1foo.txt
NA2foo_bar.jpeg
NAbar.baz.txt

And so on.

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.