I'm relatively new to bash and I have tried multiples solutions that I could find here but none of them seem to be working in my case. It's pretty simple, I have a folder that looks like this:
- images/
- 0_image_1.jpg
- 0_image_2.jpg
- 0_image_3.jpg
- 1_image_1.jpg
- 1_image_2.jpg
- 1_image_3.jpg
and I would like to move these jpg files into subfolders based on the prefix number like so:
- images_0/
- 0_image_1.jpg
- 0_image_2.jpg
- 0_image_3.jpg
- images_1/
- 1_image_1.jpg
- 1_image_2.jpg
- 1_image_3.jpg
Is there a bash command that could do that in a simple way ? Thank you
CodePudding user response:
for src in *_*.jpg; do
dest=images_${src%%_*}/
echo mkdir -p "$dest"
echo mv -- "$src" "$dest"
done
Remove both echo
s if the output looks good.
CodePudding user response:
I would do this with rename
a.k.a. Perl rename
. It is extremely powerful and performant. Here's a command for your use case:
rename --dry-run -p '$_="images_" . substr($_,0,1) . "/" . $_' ?_*jpg
Let's dissect that. At the right end, we specify we only want to work on files that start with a single character/digit before an underscore so we don't do damage trying to apply the command to files it wasn't meant for. Then --dry-run
means it doesn't actually do anything, it just shows you what it would do - this is a very useful feature. Then -p
which handily means "create any necessary directories for me as you go". Then the meat of the command. It passes you the current filename in a variable called $_
and we then need to create a new variable called $_
to say what we want the file to be called. In this case we just want the word images_
followed by the first digit of the existing filename and then a slash and the original name. Simples!
Sample Output
'0_image_1.jpg' would be renamed to 'images_0/0_image_1.jpg'
'0_image_2.jpg' would be renamed to 'images_0/0_image_2.jpg'
'1_image_3.jpg' would be renamed to 'images_1/1_image_3.jpg'
Remove the --dry-run and run again for real, if the output looks good.
Using rename
has several benefits:
- that it will warn and avoid any conflicts if two files rename to the same thing,
- that it can rename across directories, creating any necessary intermediate directories on the way,
- that you can do a dry run first to test it,
- that you can use arbitrarily complex Perl code to specify the new name.
Note: On macOS, you can install rename
using homebrew:
brew install rename
Note: On some Ones, rename
is referred to as prename
for Perl rename
.
CodePudding user response:
What about this:
$ for file in $(ls images/*.jpg | cut -d/ -f2); do
newdir="images_${file:0:1}"
[[ ! -d "$newdir" ]] && mkdir $newdir
mv "images/$file" "$newdir"
done
Assuming the image filename has single digit prefix, newdir
name is formed by appending a suffix of the first character of the filename (${file:0:1}
- 0 is the offset and length is 1). And if directory newdir
does not exist, create one. Finally, move file from old to new.
If the filename could have a multi-digit prefix like 10_image.jpg, use this:
for file in $(ls images/*.jpg | cut -d/ -f2); do
newdir="images_$(echo $file | cut -d_ -f1)"
[[ ! -d "$newdir" ]] && mkdir "$newdir"
mv "images/$file" "$newdir"
done