Bash to copy file 3 times and rename based on another file

In the below bash I am trying to copy the only text file (always only one) in /home/cmccabe/Desktop/list/QC/metrics.txt and rename each of the 3 text files according to /home/cmccabe/Desktop/test/list.txt using lines 3, 4 ,5 . This format (that is list.txt) is always 5 lines. Thank you :).

/home/cmccabe/Desktop/test/list.txt

analysis done using v1.5 by cmccabe at 02/08/17 12:19:08 PM
patients analyzed: 
00-0000_Last-First
11-1111_LastName-FirstName
22-2222_LastLastName-FirstFirstName

/home/cmccabe/Desktop/list/QC (always a single text file with data in it)

metrics.txt
linesToSkip=2
{
    for ((i=$linesToSkip;i--;)) ;do
        read
        done
files=( /home/cmccabe/Desktop/list/QC/metrics.txt )
i=0
while read -r new_name; do
  mv "${files[$i]}" "$new_name"
  (( i++ ))
}
done < /home/cmccabe/Desktop/test/list.txt

You might be better with something like this:-

LinesToSkip=2
((StartLine=$LinesToSkip+1))

files=($(cat /home/cmccabe/Desktop/list/QC/metrics.txt ))

i=1

while read -r new_name
do
   mv "${files[$i]}" "$new_name"
   ((i=$i+1))
done < <(sed -n "${StartLine},\$p" /home/cmccabe/Desktop/test/list.txt

It uses sed to get the whole file from after the LinesToSkip value. It also assumes that your list of files to rename is a white-space and/or row separated input file.

You may prefer to increment the counter in a different way, but the principle is the same.

I hope that this helps,
Robin

1 Like

As long as you're using bash (or a 1993 or later version of ksh ) and not just restricting yourself to POSIX required features and assuming that the file /home/cmccabe/Desktop/list/QC/metrics.txt contains the names of the files you want to move on the 1st three lines of that file (rather than being the name of a file that you want to copy to three other files), I'd be tempted to replace:

linesToSkip=2
{
    for ((i=$linesToSkip;i--;)) ;do
        read
        done
files=( /home/cmccabe/Desktop/list/QC/metrics.txt )
i=0
while read -r new_name; do
  mv "${files[$i]}" "$new_name"
  (( i++ ))
}
done < /home/cmccabe/Desktop/test/list.txt

with:

linesToSkip=2
{	for((i = 1; i <= linesToSkip; i++))
	do	read
	done

	while read -r new_name && read -ru4 old_name
	do	mv "$old_name" "$new_name"
	done
} < /home/cmccabe/Desktop/test/list.txt 4< /home/cmccabe/Desktop/list/QC/metrics.txt
3 Likes

A much neater solution there without needlessly creating and stepping through an array. Would you mind if I borrowed it? :wink:

Kind regards,
Robin

1 Like

Hi Robin,
Would I mind? Absolutely not! If you learn something from one of my posts, I hope you (and other readers) will learn from it and be able to use it in scripts you write. That is why I post in this forum. :smiley:

2 Likes

Thank you both for the help and helping learn. Very useful and informative :).

@Don Cragun
I am trying to understand your code and thought I did but after it executes there is no output. I only modified the last line as I thought that was why there was no output, but it appears I was mistaken. I also tried to add the "_" (in bold). Thank you :).

linesToSkip=2   # skip lines 1 and 2
{	for((i = 1; i <= linesToSkip; i++))   # start at line 3
	do	read   # read lines 3,4,5
	done   # stop

	while read -r new_name && %s\"_"\n && read -ru4 old_name   # read metrics.txt in variable
	do	mv "$old_name" "$new_name"   copy orignal metrics.txt 3 times
	done   # stop
} < /home/cmccabe/Desktop/QC/test/metrics.txt 4< /home/cmccabe/Desktop/QC/analysis.txt   # rename each metrics.txt using lines 3,4,5 of analysis.txt

/home/cmccabe/Desktop/QC/test/metrics.txt - tab-delimeted, always two lines - this is the file that is copied 3 times and renamed always using lines 3,4,5 of /home/cmccabe/Desktop/QC/analysis.txt

R_Index	ISP Loading	Pre-Enrichment	Total Reads	Read Length	Key Signal	Usable Sequence	Enrichment	Polyclonal	Low Quality	Test Fragment	Aligned Bases	Unaligned Bases	Exception
1	89	.	78402052	201	77	61	98.6	29.2	10.4	79	98.8	1.2

/home/cmccabe/Desktop/QC/analysis.txt

analysis done using v1.5 by cmccabe at 02/08/17 12:19:08 PM
patients analyzed:
00-0000_Last-First
11-1111_LastName-FirstName
22-2222_LastLastName-FirstFirstName

desired output in /home/cmccabe/Desktop/QC/test

00-0000_Last-First_metrics.txt
11-1111_LastName-FirstName_metrics.txt
22-2222_LastLastName-FirstFirstName_metrics.txt

I sincerely apologize for misleading you with my script. I paid too much attention to the code sample you provided in post #1 in this thread and not enough attention to the English description (which I found confusing). The description talks about copying files (and wasn't clear whether there was one file being copied or one file that contained the names of files to be copied), while your code was moving files whose source file names seemed to appear in one file and whose destination names seemed to appear in a second file. The fact that your code was moving (using mv ) and not copying (using cp ) seemed to confirm that there were three source files instead of one. (If there is only one source file, the last two copies will obviously fail since you're using mv instead of cp !)

If you have a single source file, and you're reading a destination name from a file, but you don't want the name of that destination file to be the name you read from the file; you need to clearly explain the transformation that is supposed to take place. (There is nothing in any of your code that makes any attempt to modify the names read from the file. And, I haven't found anything in your descriptions that talks about modifying the names found in the file other than confusing examples that don't clearly explain where the names are coming from.)

The code that you showed us (that you said produces no output) should be producing a diagnostic message because it is trying to invoke a utility named

%s"_\n && read -ru4 old_name   # read metrics.txt in variable
	do	mv old_name    copy orignal metrics.txt 3 times
	done   # stop... ... ...

continuing on until a matching double-quote character ( " ) is found to match the double-quote character that appears at the end of $new_name" . Note that the new_name variable is not defined in your modification to my code (and therefore, the expansion $new_name expands to an empty string).

Please give us a clearer description of what you are trying to do.

1 Like

The metrics.txt in /home/cmccabe/Desktop/QC/test is the file that is copied 3 times. Each one of of those 3 copies is then renamed using lines 3,4,5 in analysis.txt in /home/cmccabe/Desktop/QC with a _ in the name.

So in /home/cmccabe/Desktop/QC/test before the code is executed there is one metrics.txt . Then after the code is executed there are 3 metrics.txt that are renamed using the analysis.txt , lines 3,4,5.

The format of analysis.txt is always the same, that is it is always five lines with the first two being skipped with lines 3,4,5 having the rename lines in them.

analysis.txt in /home/cmccabe/Desktop/QC

status: complete
id names: 
00-0000_Last-First
01-0101_LastN-FirstN
02-0202_La-Fi

metrics.txt in /home/cmccabe/Desktop/QC/test , then code is executed:

desired output in /home/cmccabe/Desktop/QC/test

00-0000_Last-First_metrics.txt
11-1111_LastName-FirstName_metrics.txt
22-2222_LastLastName-FirstFirstName_metrics.txt

Does this help and thank you :).

According to the above description, we are supposed to start with two files in a directory: metrics.txt and analysis.txt . Then we are supposed to create three more copies of metrics.txt also named metrics.txt and then we are supposed to rename three of those four file and end up with four files: 00-0000_Last-First_metrics.txt , 11-1111_LastName-FirstName_metrics.txt , 22-2222_LastLastName-FirstFirstName_metrics.txt , and analysis.txt .

Doing it in this sequence is impossible because you can never have two files in one directory with the same name. We can copy a file three times to new files with different names. But, if we do that, the original metrics.txt file would still be there. (But your description above says it should not be there.) So, is what you really want correctly described by saying that you want to rename metrics.txt to be the filename derives from the third line in analysis.txt and then you want to copy that file to two more files with filenames derived from the 4th and 5th lines in analysis.txt ?

Why do you need three or four copies of the same text? Why not just link the three new names to metrics.txt using an ln command (or tell whatever program will be reading these files that the name of the file they should read is just metrics.txt )?

1 Like

The metrics.txt in / home/cmccabe/Desktop/QC/test is saved as 3 separate .txt files. Each one of of those 3 .txt files is then renamed using lines 3,4,5 in analysis.txt in /home/cmccabe/Desktop/QC with a _ in the name.

Another process that is used is expecting 3 files in home/cmccabe/Desktop/QC/test to function properly, so this is a temporary solution to that (until that process can be re-written). Thank you very much :).

home/cmccabe/Desktop/QC/test

metrics1.txt
metrics2.txt
metrics3.txt

analysis.txt in /home/cmccabe/Desktop/QC

status: complete
id names: 
00-0000_Last-First
01-0101_LastN-FirstN
02-0202_La-Fi

desired output in home/cmccabe/Desktop/QC/test after the script executes

00-0000_Last-First_metrics1.txt
11-1111_LastName-FirstName_metrics2.txt
22-2222_LastLastName-FirstFirstName_metrics3.txt

Since there is nothing to match between the two file I don't thin process substitution can be used, the names are just:

metrics1.txt = line 3 of analysis.txt
metrics2.txt = line 4 of analysis.txt
metrics3.txt = line 5 of analysis.txt

Hi.

Extraction of base filenames, then tack on the suffix:

#!/usr/bin/env bash

# @(#) s1       Demonstrate replicate file to files determined by list.

# Utility functions: print-as-echo, print-line-with-visual-space, debug.
# export PATH="/usr/local/bin:/usr/bin:/bin"
LC_ALL=C ; LANG=C ; export LC_ALL LANG
pe() { for _i;do printf "%s" "$_i";done; printf "\n"; }
pl() { pe;pe "-----" ;pe "$*"; }
em() { pe "$*" >&2 ; }
db() { ( printf " db, ";for _i;do printf "%s" "$_i";done;printf "\n" ) >&2 ; }
db() { : ; }
C=$HOME/bin/context && [ -f $C ] && $C sed cp

pl " Input data files data1, data2:"
head data[12]

pl " Results:"
for file in $( sed -n '3,5p' data2 )
do
  echo cp data1 ${file}_metrics.txt
done

exit 0

producing:

$ ./s1

Environment: LC_ALL = C, LANG = C
(Versions displayed with local utility "version")
OS, ker|rel, machine: Linux, 3.16.0-4-amd64, x86_64
Distribution        : Debian 8.7 (jessie) 
bash GNU bash 4.3.30
sed (GNU sed) 4.2.2
cp (GNU coreutils) 8.23

-----
 Input data files data1, data2:
==> data1 <==
Some text that is not important to the process.

==> data2 <==
analysis done using v1.5 by cmccabe at 02/08/17 12:19:08 PM
patients analyzed: 
00-0000_Last-First
11-1111_LastName-FirstName
22-2222_LastLastName-FirstFirstName

-----
 Results:
cp data1 00-0000_Last-First_metrics.txt
cp data1 11-1111_LastName-FirstName_metrics.txt
cp data1 22-2222_LastLastName-FirstFirstName_metrics.txt

Remove the final echo when satisfied that the filenames are correct.

Best wishes ... cheers, drl

1 Like

Thank you very much :slight_smile: