shell script to delete certain files and rename others - shell

Currently I've a lot of .mp4 files with these names:
103160626309temp1ep10.mp4
103160626309temp1ep10.mp4.mp4
148999555452temp1ep6.mp4
148999555452temp1ep6.mp4.mp4
6802547045temp1ep5.mp4
6802547045temp1ep5.mp4.mp4
335587012366temp1ep4.mp4
335587012366temp1ep4.mp4.mp4
...
I must delete all files with single .mp4 and rename .mp4.mp4 to .mp4. Do you have an idea about how can I do it?
I think about using:
for i in ./*.mp4; do
...
and
for i in ./*.mp4.mp4; do
...
But I'm afraid and I can't lose the files or damage them.
Can you help me solve this issue?
Thank you.

You don't need a separate deletion step if you just rename the .mp4.mp4 files over the .mp4 files.
(This assumes that for every .mp4 file there is a corresponding .mp4.mp4 file. If that is not the case, see the "multiple steps" instructions below.)
There are several ways to do that:
Plain shell:
for file in *.mp4.mp4; do
mv -- "$file" "${file%.mp4}"
done
(Use echo instead of mv to see what it's going to do before you run it "for real".)
If you have the perl version of rename (sometimes also known as prename or perl-rename):
rename -f 's/\.mp4\.mp4\z/.mp4/' *.mp4.mp4
(Use rename -n for a dry run.)
If you have the util-linux version of rename:
rename .mp4.mp4 .mp4 *.mp4.mp4
Beware; this will simply replace the first occurrence of .mp4.mp4 in the filenames, but hopefully that'll always be at the end of the filename.
(Again, use rename -n for a dry run.)
If you have mmv:
mmv -d '*.mp4.mp4' '#1.mp4'
If you want to do things in multiple steps:
Create a separate directory for the files you want to keep:
mkdir to-be-kept
Move all .mp4.mp4 files into that directory:
mv *.mp4.mp4 to-be-kept/
Delete all remaining .mp4 files (or move them somewhere else if you want):
rm *.mp4
Or move them somewhere else:
mv *.mp4 some/other/directory
Move the .mp4.mp4 files back:
mv to-be-kept/* .
rmdir to-be-kept
Use one of the above recipes to do the renaming.
You don't need rename -f (use plain rename instead) or mmv -d (use plain mmv instead) because in this case there's no need to overwrite existing files.

Related

How to remove the content of folders before unzipping many zips? (bash script)

I have many folders on FTP with their content, and some of them I must update from time to time. I update them by unzipping zip files I receive. Names of zips may be various, but in a zip, there is always the main folder with exactly the same name of a folder that should be updated on FTP. No more other files/folders in zips other than the main folder with its content. So I wrote a simple script below to update them:
unzip -o \*.zip
rm -f *.zip
The problem is, sometimes there are files that should be deleted in these folders - they no longer exist in zips with updates. And I realized that when I unzip and overwrite, nothing is deleted what should be. Is it possible to modify this script, to remove a whole folder before unzipping to be sure? The proper name of a folder to update is not the name of zip, but the name of the main folder in zip, and because of that I don't know how to solve this. I couldn't find an existing solution for this. Also, sometimes I upload many zips at once, and there are thousands of folders on FTP so it would be hard to write a single command for every single folder.
You can use the unzip companion program zipinfo to list the contents of the zip files. Add the pattern */ to list only directories. Then pipe to xargs to remove them.
zipinfo -1 '*.zip' '*/' | xargs rm -rf 2>/dev/null
This will remove all existing directories (which match in an existing zip file) at once. You can then run the rest of your script to extract the new ones.
You could add cut -d / -f 1 | sort -u | before xargs to filter out any subdirectories for rm, but it shouldn't matter even if there are some.
xargs splits lines by whitespace, so a directory name containing whitespace could result in a different directory being removed. For GNU xargs, you can add --delimiter='\n' to stop that (there's also --null, but zip truncates new lines in file names anyway). You can also just exclude directories containing spaces by piping through grep -v '[[:space:]]'.
Another approach which may be useful is to process one zip file at a time:
for zip in *.zip; do
dirs=$(zipinfo -1 "$zip" '*/') || continue
IFS=$'\n' read -rd '' -a dirs<<<"$dirs"
rm -rf "${dirs[#]}"
unzip -o "$zip"
done
This method is also fine with whitespace. Splitting dirs in to an array just means rm will still succeed if there is more than one directory in an archive. If zipinfo fails, it probably means the archive is corrupt or unreadable, hence || continue. You can remove that if you want to attempt extraction regardless.

Deleting specific files in a directory using bash

I have a txt file with a list of files (approximately 500) for example:
file_0_hard.msOut
file_1_hard.msOut
file_10_hard.msOut
.
.
.
file_1000_hard.msOut
I want to delete all those files whose name is not in the txt file. All of these files are in the same directory. How can I do this using bash where I read the text file and then delete all those files in the directory that are not in the text file. Help would be appreciated.
Along the lines of user1934428
There is something to say for this solution. But since we have linux at our disposal with a strong filesystem in use I hope. we can make hardlinks; The only requirement for that the destination is on the same filesystem.
So along those lines:
make a directory to store the files you want to keep.
hardlink (ln {file} {target}) ; as this does not cost extra disk space, it only stores the inode number in the new directory file.
remove all files
move the files back from their origin.
And actually this would be about the same as:
mv {files} {save spot}
remove all files
mv {save spot}/{files} back
Which does pretty much the same thing. Then again; it is a nice way to learn about the power of a hardlink.
you may try this :
cd path/dir
for f in *; do
if ! grep -Fxq "$f" pathToFile/file.txt; then
rm -r "$f"
else
printf "exists-- %s \n" ${f}
fi
done
In case you are wondering (as I did) what -Fxq means in plain English:
F: Affects how PATTERN is interpreted (fixed string instead of a regex)
x: Match whole line
q: Shhhhh... minimal printing
Assuming the directory in question is mydir
set -e
cd mydir
tmpdir=/tmp/x$$ # adapt this to your taste
mv $(<list.txt) $tmpdir
cd ..
rm -r mydir
mkdir mydir
mv $tmpdir/* mydir
rm -r $tmpdir
Basically, instead to delete those files you want to keep, you safe them, then delete everything, and then restore them. For your case, this is probably faster than doing the other way around.
UPDATE:
As Michiel commented, it is advisable that you place your tmpdir in the same file system as mydir.

Move files and prepend filename

I am having difficulty moving files and prepending to the files with Bash.
#!/bin/bash
CAT="WFS_CAT"
for FILENAME in /foo/bar/20*
do
mv "${FILENAME##*/}" "${CAT}.${FILENAME##*/}"
done;
The command errors out. It tries to move the full directory name and prepend to that instead of the individual files.
In this case, it should be much simpler to change the directory to the one your files are located in, as your move command only renames files in this directory. Do you mind trying this?
pushd /foo/bar
for FILENAME in 20*; do
echo mv "${FILENAME}" "${CAT}.${FILENAME}"
done;
popd

Script to move files

I used a script to create sub-directories based on the file names of all my mp4 files. My files and newly created sub directories, of the same name, are located in the smae sub directory. Now I need a script to move the mp4 files into each of the files corresponding sub directories. I hope this makes sense. ex: I would like to move "crank (2006).mp4" to the sub directory named "crank (2006)". I have about 1200 of these files to move to their already created sub directories. Please help.
Removing the .mp4 suffix uses %% to delete the sub-string .mp4 from the end of the $f variable.
The mkdir statement ensures that the sub-directory does exist before the mv command.
for f in *.mp4
do
subdir="${f%%.mp4}"
mkdir -p "$subdir"
mv "$f" "$subdir"
done
mmv 'smae/*.mp4' 'smae/#1/#1.mp4'
This is much safer than (noddy) scripts as mmv will check for loops, name collisions, possible problems in the move before moving any file etc.
Following code will,
find the .mp4 files in current directory,
create sub-directories based on the file names of all mp4 files,
move each of the files to corresponding sub directories
for f in *.mp4; do path=$(ls $f | rev | cut -c 5- | rev); mkdir $path; mv $f $path/. ; done
Ex: if "crank (2006).mp4" is available into current directory than new sub directory named "crank (2006)" will be created into current directory and "crank (2006).mp4" file will be moved into that sub-directory.
NOTE: instead of "mv" you can also use "cp" for copy files

Bash shell script to glob files in several directories, add to an archive and remove original file

I am trying to write a bash script that does the following:
Enumerates through list of files in a directory, that match a specified pattern
Creates a tar file containing the matching files
Removes (i.e. deletes) the matched files from their source directories
To keep things simple, I intend to use a hard coded list of directories and file patterns
This is what I have come up with so far:
#!/bin/bash
filenames[0]='/home/user1/*.foo'
filenames[1]='/some/otherpath/*.fbar'
for f in ${filenames[#]}
do
echo "$f"
done
However, I am unusure on how to proceed from this point onward. Specifically, I need help on:
How to glob the files matching the pattern $f
How to add the ENTIRE list of matching files (i.e. from all directories) to a tar file in one go
Regarding deleting the files, I am thinking of simply iterating through the ENTIRE list obtained in step 2 above, and 'rm' the actual file - is there a better/quicker/more elegant way?
PS:
I am running this on Ubuntu 10.0.4 LTS
If you want to use a loop because you have many directories, you can use the -r option to append to the tar file. You can also use --remove-files to remove files after adding them to the archive.
filenames[0]='/home/user1/*.foo'
filenames[1]='/some/otherpath/*.fbar'
for f in "${filenames[#]}"
do
tar -rvf --remove-files foo.tar $f
done
If you don't have the --remove-files option, use rm $f after the tar command.
tar(1) supports an --remove-files option that will remove the files after adding them to the archive.
Depending upon what you're trying to do with your shell globs, you might be able to ignore doing all that extra work there, too. Try this:
tar cf /dir/archive.tar --remove-files /home/user1/*.foo /some/otherpath/*.fbar

Resources