4

I have a main directory with 100 .mp4 files. I also have a set of sub-directories that goes dir_1, dir_2, dir_3, etc, up to 100. What I want do is to loop through the main directory and distribute the .mp4 files to all the subfolders, each having only one. Then, there should be two loops or one loop with two variables, whichever one is possible. This is approximately what I'm trying to achieve in a single line of code.

for file in *.mp4 & x in {1..100}; do mv $file dir_$x; done

3 Answers3

11
set -- *.mp4

for dir in dir_*/; do
    mv -- "$1" "$dir"
    shift
done

This first assigns the names of all the MP4 files to the list of positional parameters using set. It then iterates over the directories matching the pattern dir_*/. For each directory, it moves the first MP4 file from the list of positional parameters into that directory, and then shifts that MP4 file off the list.

There is no check to verify that there are as many directories as MP4 files in the above code. Would you want that, you could do

set -- *.mp4

for dir in dir_*/; do if [ "$#" -eq 0 ]; then echo 'Ran out of MP4 files' >&2 exit 1 fi mv -- "$1" "$dir" done

if [ "$#" -ne 0 ]; then echo 'Too many MP4 files' >&2 exit 1 fi

This code would work in any sh-like POSIX shell.

Kusalananda
  • 333,661
5

Use one loop, increment the counter using an arithmetic expression.

x=1
for file in *.mp4 ; do
    mv -- "$file" dir_$x/
    ((++x))
done

Double quote the file name, as mp4s can contain whitespace in their names. Use -- in front of "$file" to avoid interpreting the filename as a set of options if it starts with a dash.

Kusalananda
  • 333,661
choroba
  • 47,233
2

An other bashism:

#!/bin/bash

mp4=( ./*.mp4 ) for i in "${!mp4[@]}" do mv "${mp4[i]}" "dir_$i/" done

I wouldn't use it in your case but it's always interesting to see different methods.

Fravadona
  • 561