Rename part of file name based on exact match in contents of another file

删除回忆录丶 提交于 2019-12-02 20:16:25

问题


I would like to rename a bunch of files by changing only one part of the file name and doing that based on an exact match in a list in another file. For example, if I have these file names:

sample_ACGTA.txt
sample_ACGTA.fq.abc
sample_ACGT.txt
sample_TTTTTC.tsv
sample_ACCCGGG.fq
sample_ACCCGGG.txt
otherfile.txt

and I want to find and replace based on these exact matches, which are found in another file called replacements.txt:

ACGT    name1
TTTTTC  longername12
ACCCGGG nam7
ACGTA   another4

So that the desired resulting file names would be

sample_another4.txt
sample_another4.fq.abc
sample_name1.txt
sample_longername12.tsv
sample_nam7.fq
sample_nam7.txt
otherfile.txt

I do not want to change the contents. So far I have tried sed and mv based on my search results on this website. With sed I found out how to replace the contents of the file using my list:

while read from to; do
  sed -i "s/$from/$to/" infile ;
done < replacements.txt, 

and with mv I have found a way to rename files if there is one simple replacement:

for files in sample_*; do
  mv "$files" "${files/ACGTA/another4}"
done 

But how can I put them together to do what I would like?

Thank you for your help!


回答1:


You can perfectly combine your for and while loops to only use mv:

while read from to ; do
  for i in test* ; do
    if [ "$i" != "${i/$from/$to}" ] ; then
      mv $i ${i/$from/$to}
    fi
  done
done < replacements.txt

An alternative solution with sed could consist in using the e command that executes the result of a substitution (Use with caution! Try without the ending e first to print what commands would be executed).

Hence:

sed 's/\(\w\+\)\s\+\(\w\+\)/mv sample_\1\.txt sample_\2\.txt/e' replacements.txt

would parse your replacements.txt file and rename all your .txt files as desired.

We just have to add a loop to deal with the other extentions:

for j in .txt .bak .tsv .fq .fq.abc ; do
  sed "s/\(\w\+\)\s\+\(\w\+\)/mv 'sample_\1$j' 'sample_\2$j'/e" replacements.txt
done

(Note that you should get error messages when it tries to rename non-existing files, for example when it tries to execute mv sample_ACGT.fq sample_name1.fq but file sample_ACGT.fq does not exist)




回答2:


You could use awk to generate commands:

% awk '{print "for files in sample_*; do mv $files ${files/" $1 "/" $2 "}; done" }' replacements.txt 
for files in sample_*; do mv $files ${files/ACGT/name1}; done
for files in sample_*; do mv $files ${files/TTTTTC/longername12}; done
for files in sample_*; do mv $files ${files/ACCCGGG/nam7}; done
for files in sample_*; do mv $files ${files/ACGTA/another4}; done

Then either copy/paste or pipe the output directly to your shell:

% awk '{print "for files in sample_*; do mv $files ${files/" $1 "/" $2 "}; done" }' replacements.txt | bash

If you want the longer match string to be used first, sort the replacements first:

% sort -r replacements.txt | awk '{print "for files in sample_*; do mv $files ${files/" $1 "/" $2 "}; done" }' | bash


来源:https://stackoverflow.com/questions/24071114/rename-part-of-file-name-based-on-exact-match-in-contents-of-another-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!