4

I have a directory full of files ending with different extensions, how would I list/select only the files ending with .in and corresponding .out that share the same basename?

e.g.

file1.txt
file1.in
file2.in
file3.in
file2.out
file3.out

What I want to select from these files are:

file2.in
file2.out
file3.in
file3.out
5

Since you want .in to be paired with .out, loop through only *.in and check if there is a corresponding .out file, if so, print out both:

for f in *.in; do
  if [[ -f ${f%.in}.out ]]; then
    echo $f
    echo ${f%.in}.out
  fi
done
  • This is how I would have done it. Sweet and simple. I'd simplify it to one line though: for f in *.in; do [[ -e ${f%.in}.out ]] && echo $f ${f%.in}.out; done – rsaw Nov 26 '12 at 15:03
3

First match against all files, and check if .out file exists, then print the name:

for x in *; do
   if [[ "$x" =~ ^(.*)\.in ]] && [[ -f ${BASH_REMATCH[1]}.out ]];then 
      echo -n "$x ${x%%.in}.out "; 
   fi
done
echo
  • Daaaayumn. I had no idea BASH could save substrings to a variable like that. That BASH_REMATCH array is awesome. Niiice! PS: You could simplify that into one [[ ... && ... ]] expression. – rsaw Nov 26 '12 at 3:44
  • @ryran, [[ ... ]] & [[ ... ]] is guaranteed to be processed left-to-right. Are you sure that [[ ... && ... ]] is also so guaranteed? (Not so interesting if it merely happens now to be so processed in the current version of bash.) – dubiousjim Nov 26 '12 at 9:53
  • @dubiousjim: Yes, I'm sure that [[ ... && ...]] is processed as expected. Take a look at the bash man page under the [[ expression ]] section. – rsaw Nov 26 '12 at 14:58
  • Wouldn't it be more efficient to only select the "*.in" files, e.g. "for x in *.in; do" rather than selecting all files and then checking if the file ends with ".in"? – Daemon of Chaos Nov 26 '12 at 15:00
2

With zsh:

has_out() [[ -e $REPLY:r.out ]]
ls -ld -- *.in(+has_out)
1

You can list all those files, remove the extensions, filter duplicates and print them:

$ ls | grep '\(\.in\|.out\)$' | sed 's/....$//' \
     | sort | uniq -c | grep '^[\t ]*2' | sed 's/^[\t ]\+2 //'

Advantages:

  • does not need bash
  • does just one expensive operation - directory listing - i.e. does not need to stat(2) each file (as in [[ -f ... ]])

In case the ls already alphabetically sorts its output the sort command can be removed from the pipe. I don't know if POSIX specifies the sorting behavior of a ls.

The pipe prints all the basenames if files with .in and .out extension.

If you then need a list of the corresponding files you have to suffix the list with the previously filtered extensions. For example like this:

$ ls | grep '\(\.in\|.out\)$' | sed 's/....$//' \
     | sort | uniq -c | grep '^[\t ]*2' \
     | sed -e 's/^[\t ]\+2 //' -e 's/$/.in/ ; p; s/in$/out/'

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.