我需要您在创建批处理脚本或PowerShell脚本时编写savant的帮助,该脚本将根据平均总文件大小将一组文件从一个目录移动并划分为4个子目录。排序后,子目录在文件夹大小方面应该大致相等。
为什么我需要这个?
我想通过FFMPEG使用4台计算机进行编码,并且根据总平均大小将脚本分成4个部分(子目录)会很有帮助。
因此,假设有各种不同文件大小的电影文件总数达到100 GB,该脚本将分割电影文件并将其移动到4个子文件夹中;每个文件夹大约有25 GB。这样做将允许4台机器平等有效地编码数据总和。
在完成所有编码后,我将有2个文件,XYZ。(原始扩展名)和XYZ.264,一个可以比较2个文件并删除较大文件的脚本将非常有用,并减少了人工检查。
谢谢,我希望这是可能的。
答案 0 :(得分:1)
这似乎是一个简单的请求,但是exact partitioning is actually a really hard problem。
近似公平分区的最简单方法是简单地对所有文件(从最大到最小)进行排序,然后将它们逐个分发到 n 组中(有点像你是为纸牌游戏发卡:
# Define number of subgroups/partitions
$n = 4
# Create your destination folders:
$TargetFolders = 1..$n |ForEach-Object {
mkdir "C:\path\to\movies\sub$_"
}
# Find the movie files sort by length, descending
$Files = Get-ChildItem "C:\path\to\movies" -Recurse |Where-Object {'.mp4','.mpg','.xyz' -contains $_.Extension} |Sort-Object Length -Descending
for($i = 0; $i -lt $Files.Count; $i++)
{
# Move files into sub folders, using module $n to "rotate" target folder
Move-Item $Files[$i].FullName -Destination $TargetFolders[$i % $n]
}
如果您要包含多种文件类型,请使用Where-Object
过滤器而不是Filter
参数和Get-ChildItem
:
$Files = Get-ChildItem "C:\path\to\movies" -File -Recurse |Where-Object {'.mp4','.mpg','.xyz' -contains $_.Extension} |Sort-Object Length -Descending
答案 1 :(得分:0)
#!/bin/bash
nbr_of_dirs=4
# Go to directory if specified, otherwise execute in current directory
if [ -n "$1" ]; then
cd $1
fi
# Create output directories and store them in an array
for i in $(seq 1 $nbr_of_dirs); do
dir=dir_$i
mkdir $dir
dirs[i]=$dir
done
# For every non-directory, in decreasing size:
# find out the current smallest directory and move the file there
ls -pS | grep -v / | while read line; do
smallest_dir=$(du -S ${dirs[@]} | sort -n | head -1 | cut -f2)
mv "$line" $smallest_dir
done
请记住在执行此操作时将脚本文件保留在其他目录中。该脚本遍历每个文件,因此如果脚本也在目录中,它将被移动到其中一个子目录。
答案 2 :(得分:0)
@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
PUSHD "%sourcedir%"
:: number of subdirectories
SET /a parts=4
:: make subdirs and initialise totalsizes
FOR /L %%a IN (1,1,%parts%) DO MD "%destdir%\sub%%a" 2>nul&SET /a $%%a=0
:: directory of sourcefiles, sort in reverse-size order
FOR /f "tokens=1*delims=" %%a IN (
'dir /b /a-d /o-s * '
) DO (
REM find smallest subdir by size-transferred-in
SET /a smallest=2000000000
FOR /L %%p IN (1,1,%parts%) DO IF !$%%p! lss !smallest! SET /a smallest=!$%%p!&SET part=%%p
REM transfer the file and count the size
ECHO(MOVE "%%a" "%destdir%\sub!part!"
REM divide by 100 as actual filelength possibly gt 2**31
SET "size=%%~za"
IF "!size:~0,-2!" equ "" (SET /a $!part!+=1) ELSE (SET /a $!part!=!size:~0,-2! + $!part!)
)
popd
GOTO :EOF
我相信这些言论应该解释这个方法。原则是记录传输到每个子目录的长度,并选择最小填充作为文件的目的地(以反向大小顺序处理)
由于批次的限制为2 ^ 31,因此我选择通过砍掉最后2位数来粗略地将文件大小除以100。对于<100字节的文件,我随意将其记录为100字节。
您需要更改sourcedir
和destdir
的设置以适合您的具体情况。
为了测试目的,所需的MOVE命令仅为ECHO
。 在您确认命令正确后,将ECHO(MOVE
更改为MOVE
以实际移动文件。附加>nul
以取消报告消息(例如1 file moved
)
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "destdir=U:\destdir"
SET "spaces= "
FOR /f "delims=" %%a IN (
'dir /b /ad "%destdir%\*"'
) DO (
PUSHD "%destdir%\%%a"
FOR /f "delims=" %%f IN (
'dir /b /a-d "*.xyz" 2^>nul'
) DO (
IF EXIST "%%f.264" (
FOR %%k IN ("%%f.264") DO (
SET "sizexyz=%spaces%%%~zf"
SET "size264=%spaces%%%~zk"
IF "!sizexyz:~-15!" gtr "!size264:~-15!" (ECHO(DEL /F /Q "%%f") ELSE (ECHO(DEL /F /Q "%%f.264")
)
)
)
popd
)
GOTO :EOF
第二批将目录名扫描到%%a
,然后暂时切换到排序目录%destfile\%%a
。
在那里,我们会查找.xyz
个文件,找到每个文件,找到相应的.xyz.264
文件。
如果存在,那么我们找到文件的大小(%%~zk
或%%~zf
)并将其附加到一长串空格。通过将结果的最后15个字符作为字符串进行比较,我们可以确定哪个更长。
为了测试目的,所需的DEL命令仅为ECHO
。 在您确认命令正确后,将ECHO(DEL
更改为DEL
以实际删除文件。
如果.264
文件为filename.264
而不是filename.xyz.264
,则将{strong>每个 "%%f.264"
替换为"%%~nf.264"
({{1} }}仅选择名称部分。
要手动输入源目录名,请使用
~n
要接受源目录名作为参数,请使用
SET /p "sourcedir=Source directory "
要处理除 SET "sourcedir=%%~1"
个文件以外的所有文件,请更改
.h264
到
FOR /f "delims=" %%f IN (
'dir /b /a-d "*.xyz" 2^>nul'
) DO (
其中 FOR /f "delims=" %%f IN (
'dir /b /a-d "*.*" 2^>nul'
) DO if /i "%%~xf" neq ".h264" (
表示“所有文件”,额外*.*
语句检查文件名if
(%%f
)的扩展名是否不等于({{1无论情况如何{(1}}和%%~xf
指示“(案例 - 我 nsensitive)”