Files overwritten during cp loop - unix

I'm trying to copy 4 files that are present in Folder A (main folder) into all subfolders (1000) present in Folder A. To do this I used:
for i in *;
do
cp File1.bash $i
cp File2.sh $i
cp File3.R $i
cp File4.R $i
done
Unfortunately, the content of all the files after the cp is overwritten in destination directories. In other words files: File2.sh, File3.R and File4.R have the content of File1.bash.
Can anyone help me to solve this problem?
Thank you very much

The problem here is that the wildcard * is replaced by every file in the directory, including File2.sh,, File3.R and so on. So, at one point during the loop, $i will be 'File2.sh,' and you will execute the command cp File1.bash File2.sh, which will overwrite File2.sh (the same problem happens for the other files).
You should replace the wildcard with a command which only list directory such as ls -d */.
For instance :
for i in $(ls -d */)
do
cp File1.bash $i
cp File2.sh $i
cp File3.R $i
cp File4.R $i
done
Also , note that cp can take multiple arguments as source, so cp File1.bash File2.sh File3.R File4.R $i should do what you want. It is also less error prone as the last parameter is expected to be a directory, so cp will give an error if $i is a regular file.

The for cycle goes through all files and directories (except ones which name starts with a dot) in the current directory including the files File1.bash, File2.s, File3.R, File4.R so sooner or later they will appear in the destination of the cp commands and get overwritten.
There are multiple ways how to resolve the problem:
Expand just directories
for i in */ ; do
cp File1.bash File2.sh File3.R File4.R "$i"
done
Test if the destination is a directory
for i in * ; do
if test -d "$i" ; then
cp File1.bash File2.sh File3.R File4.R "$i"
fi
done
Compared to the first answer this code does not need to call an additional external command (ls) and it is not a good idea to parse output of ls anyway :) (It could contain some unexpected content.)
Move the source files to a different place
For example move the files to directory called .template (the dot is important) or to a directory outside the current directory (../template):
mv File1.bash File2.sh File3.R File4.R .template
Changed script (will not cycle through the hidden .template):
source=.template
for i in * ; do
cp "$source/File1.bash" "$i"
cp "$source/File2.sh" "$i"
cp "$source/File3.R" "$i"
cp "$source/File4.R" "$i"
done
Using double quotes
It is a good idea to enclose string where you expand variables between double quotes. Then the script will correctly work with string containing spaces newlines etc. too.

Related

Rename files in a directory the simplest way in a script

I want to write a script that add '0' at the end of the files that doesn't have it.
This is what I wrote:
#!/bin/bash
for file in $1
do
echo $file
ls $file | grep "\0$"
if ["$?"="1"]
then
fi
done
I don't know hot to target the files in a way I can rename them
for file in *[!0]; do mv "$file" "${file}0"; done
For each name that does not end 0, rename it so it does. Note that this handles names with spaces etc in them.
I want to give the script a directory, and it will rename the files in it that do not end in 0. How can I use this in a way I can tell the script which directory to work with?
So, make the trivial necessary changes, working with a single directory (and not rejecting the command line if more than one directory is specified; just quietly ignoring the extras):
for file in "${1:?}"/*[!0]; do mv "$file" "${file}0"; done
The "${1:?}" notation ensures that $1 is set and is not empty, generating an error message if it isn't. You could alternatively write "${1:-.}" instead; that would work on the current directory instead of a remote directory. The glob then generates the list of file names in that directory that do not end with a 0 and renames them so that they do. If you have Bash, you can use shopt -s nullglob you won't run into problems if there are no files without the 0 suffix in the directory.
You can generalize to handle any number of arguments (all supposed to be directories, defaulting to the current directory if no directory is specified):
for dir in "${#:-.}"
do
for file in "$dir"/*[!0]; do mv "$file" "${file}0"; done
done
Or (forcing directories):
for dir in "${#:-.}"
do
(cd "$dir" && for file in *[!0]; do mv "$file" "${file}0"; done)
done
This has the merit of reporting which arguments are not directories, or are inaccessible directories.
There are endless variations of this sort that could be made; some of them might even be useful.
Now, I want to do the same but, instead of the file ending with '0', the script should rename files that do not end with '.0' so that they do end with '.0'?
This is slightly trickier because of the revised ending. Simply using [!.][!0] is insufficient. For example, if the list of files includes 30, x.0, x0, z.9, and z1, then echo *[!.][!0] only lists z1 (omitting 30, x0 and z.9 which do not end with .0).
I'd probably use something like this instead:
for dir in "${#:-.}"
do
(
cd "$dir" &&
for file in *
do
case "$file" in
(*.0) : skip it;;
(*) mv "$file" "${file}0";;
esac
done
)
done
The other alternative lists more glob patterns:
for dir in "${#:-.}"
do
(cd "$dir" && for file in *[!.][!0] *.[!0] *[!.]0; do mv "$file" "${file}0"; done)
done
Note that this rapidly gets a lot trickier if you want to look for files not ending .00 — there would be a 7 glob expressions (but the case variant would work equally straight-forwardly), and shopt -s nullglob becomes increasingly important (or you need [ -f "$file" ] && mv "$file" "${file}.0" instead of the simpler move command).

Name a directory with specific pattern in Unix

How can i rename a directory by interchanging the digits and word in directory name.
e.g.
FRA-DEV_007583-K4C-rdf-1
FRA-DEV_007583-K4C-source-8
FRA-DEV_007584-K4C-rdf-19
FRA-DEV_007584-K4C-rdf-8
output should be
FRA-DEV_007583-K4C-1-rdf
FRA-DEV_007583-K4C-8-source
FRA-DEV_007584-K4C-9-rdf
FRA-DEV_007584-K4C-8-rdf
If you have all those files in the same directory, with no other files in there, you could use this script:
#! /bin/bash
nums=(`ls $1 | cut -d- -f5`)
words=(`ls $1 | cut -d- -f4`)
files=(`ls $1 | cut -d- -f1-3`)
complete_files=(`ls $1`)
len=${#complete_files[#]}
for (( i=0; i<${len}; i++ ));
do
newname=${files[$i]}-${nums[$i]}-${words[$i]}
mv $1${complete_files[$i]} $1$newname
done
Save this script as rename.sh in a directory OUTSIDE of the one where your files are. Then execute: bash rename.sh path/to/your/files/ don't forget the final slash, and make a backup first just in case.

Compare directory name to string

I've created this very simple batch file for the sake of testing a concept I'm hoping to utilize. I need to recursively delete all of one type of file except in folders with a specific name. Here's my code:
:recur
FOR /f %%a IN ('DIR /b') DO (
IF EXIST %%a\NUL (
IF ["%%a" NEQ "subtest2"] (
ECHO %%a
CD %%a
CALL :recur
CD ..
)
)
COPY "*.cfm" "*_copy.cfm"
REM DEL "*_copy*.cfm"
)
Right now I'm just testing using copy instead of delete. Basically, this should create a copy of all the .cfm files except in the folder "subtest2". Right now it's recursively making the copies everywhere, including subtest2. How do I stop this?
The structure of my base directory is:
TestFolder
---subtest1
------test.pdf
------test.txt
------test.cfm
---subtest2
------test.pdf
------test.txt
------test.cfm
---test.pdf
---test.txt
---test.cfm
---recur.bat
The square brackets are not balanced on both sides of the IF comparison, so it can never match. The brackets are not part of the IF syntax. If present, they simply become part of the string that is compared. The same is true for any enclosing quotes. Remove the square brackets, and it will work (assuming there are no other problems)
Here is a simple method to accomplish your goal. I've prefixed the DEL command with ECHO for testing purposes:
for /r /d %%F in (*) do echo %%F\|findstr /liv "\\subtest2\\" >nul && echo del "%%F\*.cfm"
The FOR /R /D simply recurses all folders. The full path of each folder is piped into a FINDSTR command that looks for paths that do not contain a \subtest2 folder. The ECHO DEL command is only executed if the \subtest2\ folder is not found in the path.
Remove the last ECHO when you have confirmed the command gives the correct results.
Change %%F to %F if you want to run the command on the command line instead of in a batch file.
for f in `find . -path YOURDIR -prune -o print`
do
rm whateveryouwanttodelete
done
the find command in backticks finds all files but ignores the directory -prune you want to ignore. Then in the body of the loop you nuke the files. You can do even better with
find . -path YOURDIR -prune -o -print0 | xargs -0 rm -f
no need for the loop. DISCLAIMER: I haven't tested it so perhaps you want to start adopting it with cp instead of rm.
You can try this:
#echo off&setlocal
for /r /d %%i in (*) do (
pushd "%%i"
echo(%%i|findstr /ri "\\subtest2$" || COPY "*.cfm" "*_copy.cfm"
popd
)

Recursive logic in shell scripts

Here is a problem that i need solution for:
Say file A contains names of files B,C,D. And file B contains file names E,F,G etc. File C contains names of files H,I,J and so on......
I have to parse the files starting from A ,and copy the files mentioned in A to dir DIR. I wanna do the same parsing on all the child files B,C,D and get their child files into my dir DIR. This should go on until i reach the last file say Z which doesn't contain any other file names.
How do i do that?
I wanna do the whole thing in a single script and any further optimization would be appreciated.
Thanks in advance.
If the files contain other data than file names more parsing could be necessary.
DIR="$HOME/DIR"
startfile="a"
counter=0
copy_to_dir ()
{
while read line ; do
if [ -f "$line" ] ; then
cp "$line" "$2" && ((counter++))
copy_to_dir "$line" "$2" # recurse
fi
done < "$1"
} # ---------- end of function copy_to_dir ----------
if [ -f "$startfile" -a -d "$DIR" ] ; then
copy_to_dir "$startfile" "$DIR" # start copying
fi
printf "'%s' : %d files copied to '%s'\n" "$0" $counter "$DIR"

Unix - Nested Loop. One loop to untar then another to inspect each file in directory

I'm trying to loop round a load of tar files and then move the extracted files into a new folder, inspect them and delete them before moving onto the next tar.
Code is below:
for i in *
do
tar -zxvf $i
mv *TTF* encoded
cd encoded
for j in *
do
echo $j
done
rm -f *TTF*
cd ..
done
When it gets to the nested loop, it asks if I want to display all x possibilities. Clearly something is going wrong. Any ideas?
Did you write this in a text editor, then try to paste it into a terminal, by any chance? Did you use tabs to indent the lines? If so, try changing tabs to spaces, or just save the file as a shell script and then run it.
(The tab key invokes completion, which displays the "display all x possibilities" message if there are lots of completions that match.)
Run the 'cd' command and its following actions in a sub-shell, which means you don't have to do 'cd ..'. Also, it would probably be better to extract each tar file directly in the sub-directory.
for i in *.tar.gz
do
mkdir encoded
(
cd encoded
tar -zxvf ../$i
for j in *
do
echo $j
done
)
rm -fr encoded
done
This assumes only that the tar file doesn't contain any names with '..' in the paths of the files, which is very uncommon.
echo "Enter no of terms" read count for i in $(seq 1 $count) do t=expr $i - 1 for j in $(seq $t -1 0) do echo -n " " done j=expr $count + 1 x=expr $j - $i for k in $(seq 1 $x) do echo -n "* " done echo ""

Resources