I have a file strings.txt with a list of strings:


And I have many files in a directory with names like:


I need to move only those files whose names start with a pattern from strings.txt. So far I've tried using xargs with mv:

cat strings.txt | xargs -I % mv %*faa ./Data

But mv doesn't see %*faa as a regex and tries to find files with this exact name (and of course it can't find any). I've also tried using ls but it works the same way:

cat strings.txt | xargs -I {} ls {}*faa

So how can I do that?

  • Unix&Linux StackExchange isn't a place where you can get people doing your (home)work for free. It's a place where you submit what you already tried, the error you met, and then people will help you making it work. So please, provide that you tried and we will try helping you. – binarym Mar 12 '20 at 15:39
  • well, I've tried different things. for example, neither this cat strings.txt | xargs -I {} ls {}*faa nor this one cat strings.txt | xargs -I % mv %*faa ./Data didn't work. – Some student Mar 12 '20 at 15:45
  • @Somestudent Please edit your question and add your attempted solution there instead of writing a comment. Explain how it doesn't work. Show the error message or wrong output or describe the wrong result. – Bodo Mar 12 '20 at 15:48

This is a possible solution:

cat strings.txt | xargs -I '%' find . -type f -name "%*" -exec mv -t your_path {} +

Moved GCA_000007405.1_ASM740v1_protein.faa that is the only match.

xargs bash -O nullglob -c '
    for prefix do
        set -- "$prefix"*.faa
        [ "$#" -gt 0 ] && mv -- "$@" ./Data
    done' sh <strings.txt

This uses xargs to give the strings in strings.txt to an in-line bash script in batches.

For each string, the in-line bash script tests whether there is any filenames matching the string in the current directory (adding *.faa to the end to create a pattern), and if so, these are moved to the Data directory, which is assumed to already exist.

The nullglob shell option is set for the in-line script to make sure that if the constructed pattern does not match anything, the pattern is removed (rather than retained unexpanded).

I'm testing explicitly whether the pattern matches because there are strings in your example that does not match any of the filenames that you show. This is done by setting the list of positional parameters to the result of the glob expansion and then testing whether the list's length ($#) is greater than zero.

You could obviously also have done this without xargs like so:

shopt -s nullglob

while IFS= read -r prefix; do
    set -- "$prefix"*.faa
    [ "$#" -gt 0 ] && mv -- "$@" ./Data
done <strings.txt

This would still fail if one of your prefix strings happened to match many thousands of filenames (the mv command line would become too long). If that is the case, you would possibly use

shopt -s nullglob

while IFS= read -r prefix; do
    set -- "$prefix"*.faa
    if [ "$#" -gt 0 ]; then
        printf '%s\n' "$@" | xargs sh -c 'mv -- "$@" ./Data' sh
done <strings.txt

This is assuming that neither prefix strings nor filenames contain embedded newlines.


No need for xargs or anything complicated. If all of the files are in the same directory, it should be enough to do:

$ while read string; do 
    mv "$string"_* ./Data; 
  done < strings.txt 

Or, if your names can start with a - or you might have other weird characters in them, use:

$ while read -r string; do 
    mv -- "$string"_* ./Data; 
  done < strings.txt 

Quick and dirty hack: cat strings.txt| xargs -i sh -c 'mv {}* your/path/'>&/dev/null

Less more correct hack: export dir=$(ls -1); cat strings.txt| xargs -i sh -c 'grep "^{}" <<<$dir && mv {}* your/path'; dir=""

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.