0

I have many files (with a .fna extension). I need to move them, if they contain the character > a certain number of times.

I know which files contain > a certain number of times (9 times in this example) with this command.

grep -c ">" *.fna | grep ":9"

Which gives an output like this

5242_ref.fna:9
9418_ref.fna:9

But I don't know how to move those files to another folder.

Thanks.

hola
  • 91
  • If a line contains two symbols (>>), does it count only once (as grep -c would do), or twice (as "contains > a certain number of times" would do? – Jeff Schaller Apr 02 '20 at 20:03
  • counts as twice – hola Apr 02 '20 at 20:22
  • I've updated my answer accordingly. You might update your Q to remove the misleading "grep" output as what you intend is different -- grep counts matching lines, not matching bytes. – Jeff Schaller Apr 02 '20 at 20:27
  • Relating only (for the grep occurrence vs line background): https://unix.stackexchange.com/q/398413/117549 – Jeff Schaller Apr 02 '20 at 20:27

2 Answers2

2

By combining egrep (called with the -l option so that positive matches return only filenames) with xargs you can perform arbitrary commands based on text matches:

[gnubeard@mothership: ~/mver]$ ls
destination  example.fna  non_example.fna
[gnubeard@mothership: ~/mver]$ cat example.fna
>>>>>>>>>
[gnubeard@mothership: ~/mver]$ cat non_example.fna
<<<<<<<<<
>>
[gnubeard@mothership: ~/mver]$ egrep -l '>{9}' *.fna | xargs -I{} mv {} destination/
[gnubeard@mothership: ~/mver]$ ls
destination  non_example.fna
[gnubeard@mothership: ~/mver]$ ls destination/
example.fna

Edit: This doesn't match files with a certain number of instances of a character, if they're separated by other characters or newlines. I've created this short script that should operate as desired.

#! /usr/bin/env bash

DESTINATION=./destination
MATCH_CHAR='>'
MATCH_COUNT=9

for FILE in $1; do
  MATCHES=$(grep -o $MATCH_CHAR $FILE | wc -l)
  if [ $MATCHES -eq $MATCH_COUNT ]; then
    mv $FILE $DESTINATION
  fi
done

Example:

[gnubeard@mothership: ~/mver]$ ls
destination  example.fna  mver  non_example.fna
[gnubeard@mothership: ~/mver]$ cat example.fna
>>
>>foo>>bar>>>baz
[gnubeard@mothership: ~/mver]$ cat non_example.fna
<<<<<<<<<
>>
[gnubeard@mothership: ~/mver]$ ./mver *.fna
[gnubeard@mothership: ~/mver]$ ls destination/
example.fna
[gnubeard@mothership: ~/mver]$ ls
destination  mver  non_example.fna
gnubeard
  • 309
  • liked the egrep solution. however, as it is it searchs for >>>>>>>>> and I need to move files with > 9 times not necessarily grouped, but anywhere in the file. what can I do? – hola Apr 02 '20 at 20:20
  • 1
    This does answer the question as stated. However there is the bug that @JeffSchaller found. Time for another question. – ctrl-alt-delor Apr 02 '20 at 20:26
  • I've updated with a solution that'll work regardless of separating characters/newlines. – gnubeard Apr 02 '20 at 20:52
1

You could use zsh with its expression-as-a-glob-qualifier to select files that only have nine such symbols:

$ hasnine() { [[ $(tr -dc '>' < "$REPLY" | wc -c) -eq 9 ]]; }
$ mv *.fna(+hasnine) location/

The first line defines a function whose purpose is to create a true/false filter for files that have nine > symbols in them. The tr command acts on its input, expected in the REPLY variable, deleting anything that is not a >, then asks wc to count the number of resulting characters, and then compares that output to 9.

The second line executes the mv command for matching *.fna files to the (example) location directory. Matching *.fna files also have to pass the expression qualifier, which is given as the name of the function we defined.

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255