Rename all files in a folder - linux

I'm on a linux, and a download a lot of funny pictures. Unfortunately, I'm left with a bunch of duplicate names like download (1) and image.jpeg. I would like to change them all to something a bit more helpful.
Is there a way to (preferably using bash) to rename all files to sequential 4 digit numbers with leading zeroes?
Eg:
0001
0002
0003
0004
....

The code snippet provided in the previous answer, is an elegant way of doing it but a typo or a shell incompatibility may cause it not to function properly.
please try the code below instead. It does the same thing but every shortcut has been explicitly written with debugging echo commands in the loop.
counter=1
cd /my/image/directory
for f in $(ls -1)
do
new_filename=$(printf "%04d" ${counter})
echo "renaming ${f} ..to.. ${new_filename}"
mv ${f} ${new_filename}
(( counter=${counter}+1 ))
done
the screen output will be a little chatty. if you have too many files, you might want to add | tee screen.out to the end of the line with done command. So that you can go back and see what happened to which file recorded in the screen.out.

I created my own tool to do this. It also maintains file extensions, which I did not mention, but should probably be included. Here is the code:
#!/bin/sh
dir=$1
cd $dir
echo "Renaming all files in $dir."
COUNTER=1
for i in `ls -1`
do
extension=${i##*.}
mv "$i" "$COUNTER.$extension"
echo "$i ==> $COUNTER.$extension"
COUNTER=$(expr $COUNTER + 1 )
done
It does not (at the time of writing) include the leading zeroes, but it gets the job done.

As long as you don't care which file is renamed to what, it's easy :)
counter=1
for f in *; do
mv "$f" "$( printf "%04d" $((counter++)) )"
done

Trying to rename all files with suffix .bash to suffix .sh in a folder is easily done with
rename .bash .sh *.bash

Related

Loop over files with extension does not work

I have several Name*.txt files in /home/user/my/path/to/my/data/, among other files with different extensions. I would like to loop over them, then use the individual file names in the code, therefore common solutions like this won't work, since the varible '$f', within each loop, stores the whole path together with the file name. I need them separately, to perform something like the "example taks" below. My attempts:
Attempt #1:
#!/bin/bash
datapath="/home/user/my/path/to/my/data/"
outpath="/home/user/my/path/to/my/outputs/"
for f in $(ls $datapath"Name*.txt"); do
echo $f
...
cp $datapath$f $outpath"example_task"${f:0:10}
done
This didn't work:
ls: cannot access /home/user/my/path/to/my/data/Name*.txt: No such file or directory.
Although running ls /home/user/my/path/to/my/data/Name*.txt on the terminal works perfectly fine. I can't understand why.
Attempt #2:
#!/bin/bash
datapath="/home/user/my/path/to/my/data/"
outpath="/home/user/my/path/to/my/outputs/"
for f in $datapath"Name*.txt"; do
echo $f
...
cp $datapath$f $outpath"example_task"${f:0:10}
done
Here, each $f contains the full list of files ls Name*.txt would normally return, and not one at a time as one would expect.
How do I do this? Any suggestions will be much appreciated.
Maybe I am on the wrong path here but this worked for me.
#!/bin/bash
datapath="/home/user/my/path/to/my/data/"
outpath="/home/user/my/path/to/my/outputs/"
cd $datapath
for f in ./*.txt; do
file=$(echo $f | cut -d '/' -f 2)
echo $file
...
cp $f $outpath"example_task"$file
done
cd

Modify file name using Python Script

I have many files in my folder with names 1.jpg-xyz, 1.jpg-abc, 2.jpg-qwe etc. I particularly want to move .jpg to the end of each image's name. I can't do it manually since these are thousands in number. I can't get rid of xyz etc after .jpg in the current name since they have important information. So only option I have is to shift .jpg to end. Can somebody tell me what command or script should I use to do that?
This should work:
find *jpg* | while read f ; do g=$(echo "$f" | sed s/\.jpg//) ; echo "mv $f ${g}.jpg" ; done
If the mv commands echoed look like what you want then remove the echo "" around it and re-run.
The below bash code will list all files with .jpg- and move them to -.jpg
re='([^.]+)\.jpg(-.*)'
for file in *.jpg-*
do
if [[ $file =~ $re ]]
then
mv $file "${BASH_REMATCH[1]}${BASH_REMATCH[2]}.jpg"
fi
done

Bash Script to replicate files

I have 25 files in a directory. I need to amass 25000 files for testing purposes. I thought I could just replicate these files over and over until I get 25000 files. I could manually copy paste 1000 times but that seemed tedious. So I thought I could write a script to do it for me. I tried
cp * .
As a trial but I got an error that said the source and destination file are the same. If I were to automate it how would i do it so that each of the 1000 times the new files are made with unique names?
As discussed in the comments, you can do something like this:
for file in *
do
filename="${file%.*}" # get everything up to last dot
extension="${file##*.}" # get extension (text after last dot)
for i in {00001..10000}
do
cp $file ${filename}${i}${extension}
done
done
The trick for i in {00001..10000} is used to loop from 1 to 10000 having the number with leading zeros.
The ${filename}${i}${extension} is the same as $filename$i$extension but makes more clarity over what is a variable name and what is text. This way, you can also do ${filename}_${i}${extension} to get files like a_23.txt, etc.
In case your current files match a specific pattern, you can always do for file in a* (if they all are on the a + something format).
If you want to keep the extension of the files, you can use this. Assuming, you want to copy all txt-files:
#!/bin/bash
for f in *.txt
do
for i in {1..10000}
do
cp "$f" "${f%.*}_${i}.${f##*.}"
done
done
You could try this:
for file in *; do for i in {1..1000}; do cp $file $file-$i; done; done;
It will append a number to any existing files.
The next script
for file in *.*
do
eval $(sed 's/\(.*\)\.\([^\.]*\)$/base="\1";ext="\2";/' <<< "$file")
for n in {1..1000}
do
echo cp "$file" "$base-$n.$ext"
done
done
will:
take all files with extensions *.*
creates the basename and extension (sed)
in a cycle 1000 times copyes the original file to file-number.extension
it is for DRY-RUN, remove the echo if satisfied

Shell script best way to remove files not in a pair

I have a set of files that come in pairs:
/var/log/messages-20111001
/var/log/messages-20111001.hash
I've had several of these rotate away and now I'm left with a ton of /var/log/messages-201110xx.hash files with no associated log. I'd like to clean up the mess, but I'm uncertain how to remove a file that isn't part of a "pair". I can use bash or zsh (or any LSB tool, really). I need to remove all the .hash files that don't have an associated log.
Example
/var/log/messages-20111001.hash
/var/log/messages-20111002.hash
/var/log/messages-20111003.hash
/var/log/messages-20111004.hash
/var/log/messages-20111005
/var/log/messages-20111005.hash
/var/log/messages-20111006
/var/log/messages-20111006.hash
Should be reduced to:
/var/log/messages-20111005
/var/log/messages-20111005.hash
/var/log/messages-20111006
/var/log/messages-20111006.hash
for file in *.hash; do test -f "${file%.hash}" || rm -- "$file"; done
Something like this?
for f in /var/log/messages-????????.hash ; do
[[ -e "${f%.hash}" ]] || rm "$f"
done

Linux: Move 1 million files into prefix-based created Folders

I have a directory called "images" filled with about one million images. Yep.
I want to write a shell command to rename all of those images into the following format:
original: filename.jpg
new: /f/i/l/filename.jpg
Any suggestions?
Thanks,
Dan
for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done;
The ${i:0:1}/${i:1:1}/${i:2:1} part could probably be a variable, or shorter or different, but the command above gets the job done. You'll probably face performance issues but if you really want to use it, narrow the *.* to fewer options (a*.*, b*.* or what fits you)
edit: added a $ before i for mv, as noted by Dan
You can generate the new file name using, e.g., sed:
$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'
t/e/s/test.jpg
So, you can do something like this (assuming all the directories are already created):
for f in *; do
mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')"
done
or, if you can't use the bash $( syntax:
for f in *; do
mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`"
done
However, considering the number of files, you may just want to use perl as that's a lot of sed and mv processes to spawn:
#!/usr/bin/perl -w
use strict;
# warning: untested
opendir DIR, "." or die "opendir: $!";
my #files = readdir(DIR); # can't change dir while reading: read in advance
closedir DIR;
foreach my $f (#files) {
(my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/;
-e $new_name and die "$new_name already exists";
rename($f, $new_name);
}
That perl is surely limited to same-filesystem only, though you can use File::Copy::move to get around that.
You can do it as a bash script:
#!/bin/bash
base=base
mkdir -p $base/shorts
for n in *
do
if [ ${#n} -lt 3 ]
then
mv $n $base/shorts
else
dir=$base/${n:0:1}/${n:1:1}/${n:2:1}
mkdir -p $dir
mv $n $dir
fi
done
Needless to say, you might need to worry about spaces and the files with short names.
I suggest a short python script. Most shell tools will balk at that much input (though xargs may do the trick). Will update with example in a sec.
#!/usr/bin/python
import os, shutil
src_dir = '/src/dir'
dest_dir = '/dest/dir'
for fn in os.listdir(src_dir):
os.makedirs(dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/')
shutil.copyfile(src_dir+'/'+fn, dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/'+fn)
Any of the proposed solutions which use a wildcard syntax in the shell will likely fail due to the sheer number of files you have. Of the current proposed solutions, the perl one is probably the best.
However, you can easily adapt any of the shell script methods to deal with any number of files thus:
ls -1 | \
while read filename
do
# insert the loop body of your preference here, operating on "filename"
done
I would still use perl, but if you're limited to only having simple unix tools around, then combining one of the above shell solutions with a loop like I've shown should get you there. It'll be slow, though.

Resources