13

I have some files in a directory as follows.

source_dir: 
ABCD.HRA.0014.2.200.png
ABCD.HRA.0015.2.200.png
ABCD.HRA.0016.2.200.png
MMNP.HRA.0016.2.200.png

I also have a text file with following content.

text.txt:

ABCD.HRA.0014
ABCD.HRA.0015

Now is there any way I can transfer the files as per the string mentioned in text.txt. After command, source dir and dest_dir should be as follows.

source_dir: 
ABCD.HRA.0016.2.200.png
MMNP.HRA.0016.2.200.png

dest_dir: 

ABCD.HRA.0014.2.200.png
ABCD.HRA.0015.2.200.png
2
  • how many items is there in the source_dir? it looks big
    – user986805
    Nov 11, 2019 at 12:02
  • 1
    Wouldn't that be moving, not copying? Nov 12, 2019 at 1:13

7 Answers 7

14

grep -f allows you to use text.txt as a source for patterns.

#!/bin/bash  
for i in source_dir/*.png; do  
  if grep -Fq -f text.txt <<< "$i"; then  
    mv -t dest_dir "$i"  
  fi  
done  
$ ls
dest_dir  script.sh  source_dir  text.txt

grep options:

  • -F Interpret patterns as fixed strings, not regular expressions.
  • -q Do not write anything to stdout.
  • -f Obtain patterns from FILE, one per line.

Here strings:

  • <<< A variant of here documents, does variable expansion before sending the string.

mv options:

  • -t Move all source arguments into -t directory.
9
  • here xx is a random don't care string. But it seems like in your answer xx on both sides is fixed.
    – Stupid420
    Nov 11, 2019 at 9:47
  • are there underscores in those parts?
    – user986805
    Nov 11, 2019 at 10:00
  • no. It should be like 'dontcaretext{stringtofind}dontcaretext'. the dontcaretext can be anything and of any length and is not same in all files.
    – Stupid420
    Nov 11, 2019 at 10:07
  • I have edited my question and provided the real example.
    – Stupid420
    Nov 11, 2019 at 10:23
  • Bu using the above comment, it moves all the files instead of just those mentioned in text.txt.
    – Stupid420
    Nov 11, 2019 at 10:27
11

Your files all seem to end in .2.200.png, hence we can use the input file only:

while read line ; do
  mv "source_dir/${line}.2.200.png" destination_dir/
done < text.txt
2
  • wow very simple, good!
    – Lety
    Nov 11, 2019 at 11:09
  • Even simpler: taking the OP to the letter, mv "source_dir/*${line}*" destination_dir/ (+1)
    – gboffi
    Nov 12, 2019 at 14:53
4

One way, if you know all the filenames in the file contain no whitespace, is like this:

cp $(cat text.txt) targetdir/

I always add the / at the end - this makes the command fail if targetdir doesn't exist and is a directory; you want that to happen, because otherwise you may end up copying all the files into one called targetdir (actually, I think that doesn't happen anymore in modern bash; I'm that old)

If each line in the file contains one filename, which may contain whitespace, the this method works, at least in ksh and newer versions of bash:

cat text.txt | while read l
do
cp "$l" targetdir/
done

And if that doesn't work in your version of bash, then this does (note the < text.txt after done):

while read l
do
cp "$l" targetdir/
done < text.txt
1
  • +1 for correcting my goof. To clarify a detail, though, if target did doesn’t exist and the slash is omitted, each file overwrites the previous, leaving only the last file copied.
    – WGroleau
    Nov 12, 2019 at 16:02
4

Update: j4nd3r53n is correct: this won’t work for any file names that contain whitespace. Apologies for my “senior moment”—what I intended (and have often done) was to pipe it to while as I have shown after. I will not delete the wrong one as it may provide a useful warning to those that might try it.

One more flawed method (see warning above):

for STR in $(cat file); do # flawed
  cp "source/*${STR}*" dest
done

Here is the method I originally meant:

cat file | while read STR # better
  cp "source/*${STR}*" dest
done
6
  • The problem with this method is, if the filenames do have whitespace embedded, it won't work.
    – j4nd3r53n
    Nov 12, 2019 at 8:58
  • Not so. I have done it often. The quote marks cause everything between them to be treated as a unit.
    – WGroleau
    Nov 12, 2019 at 15:31
  • $(cat file) will return a string of words separated by spaces, which STR will pick up as individual tokens. The quotes are not in effect at this point.
    – j4nd3r53n
    Nov 12, 2019 at 15:35
  • @j4nd3r53n, I’d be tempted to believe you if I hadn’t seen it work hundreds of times in my forty years of software engineering and home tinkering.
    – WGroleau
    Nov 12, 2019 at 15:46
  • For anyone reading these comments, @j4nd3r53n is correct—I have edited a disclaimer into my answer.
    – WGroleau
    Nov 12, 2019 at 15:54
4

Another way using find:

find source_dir -type f | while read file; do  name=$(basename $file); grep ${name%.2.200.png} text.txt && mv -v $file dest_dir; done 

Find file in source_dir and loops over the list.

For each file runs grep in text.txt. It is necessary to get the file name using basename command to exclude source_dir folder.

Finally if grep returns true, moves file in dest_dir.

Adding -v to mv command and removing -q to grep command, you can see the action.


Here is an examples of How to use it:

$ ls source_dir
ABCD.HRA.0014.2.200.png  MMNP.HRA.0016.2.200.png  shrr.2.200.png
ABCD.HRA.0015.2.200.png  sghd.2.200.png
ABCD.HRA.0016.2.200.png  shdj.2.200.png

$ cat text.txt
ABCD.HRA.0014
ABCD.HRA.0015

$ mkdir dest_dir

$ find source_dir -type f | while read file; do name=$(basename $file); grep ${name%.2.200.png} text.txt && mv -v $file dest_dir; done
ABCD.HRA.0014
`source_dir/ABCD.HRA.0014.2.200.png' -> `dest_dir/ABCD.HRA.0014.2.200.png'
ABCD.HRA.0015
`source_dir/ABCD.HRA.0015.2.200.png' -> `dest_dir/ABCD.HRA.0015.2.200.png'
8
  • Can you confirm it? I used it and it copies no file in destination directory
    – Stupid420
    Nov 11, 2019 at 10:15
  • I added options that view the action. On my Linux Redhat system it works, but for now I don't have the possibility to test it on ubuntu
    – Lety
    Nov 11, 2019 at 10:51
  • Updated with new file pattern.
    – Lety
    Nov 11, 2019 at 11:05
  • 1
    If source has subdirectories containing some of the files, you need find but if not, other methods will be faster.
    – WGroleau
    Nov 11, 2019 at 16:56
  • This works but moves only the first file in text.txt.
    – Stupid420
    Nov 11, 2019 at 23:44
3

A bit convoluted, but you could use awk (which in this case does nothing more than reading the file out line by line)

awk '{system("mv " $0 "* dest_dir/")}' text.txt 

which basically says 'for each line, do mv BASE_STR* dest/'

But the approach is very dependent on your specific use case, this might not work if you want to make it more generic.

0

Try This :

cat <list_file> | while read FILENAME; do ls -1 | grep $FILENAME | xargs -I xxx -P 0 cp -v xxx ../dest_dir ; done;
1
  • 3
    It would be better if you can a little bit of explanation.
    – Kulfy
    Feb 17, 2021 at 15:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .