是否可以通过一些复杂的逻辑对文件进行分组,然后用bash/fish进行操作?

Is it possible to group files by some complex logic and then operate them with bash/fish?

我下载了一些视频,其中一些被分成了几个部分:

001 aaaa part1.mp4
001 aaaa part2.mp4
002 bbbb part1.mp4
003 cccc part1.mp4
003 cccc part2.mp4
004 dddd part1.mp4
004 dddd part2.mp4
005 eeee part1.mp4
006 ffff part1.mp4
007 gggg part1.mp4
008 hhhh part1.mp4

以上文件中,001 ...003 ...004 ...是拆分文件,其他的不是,即使名称中有part1.mp4

我想用一些工具找到并合并拆分文件,比如 mp4box

为了做到这一点,我需要做:

  1. 在开头按每个文件的编号对文件进行分组,包含多个文件的组就是我需要的部分文件。所以会找到001/003/004文件。

  2. 通过删除 part?.mp4 为它们生成新文件名。对于001 aaaa part1.mp4001 aaaa part2.mp4,它会生成001 aaaa.mp4

  3. 调用mp4box -add "001 aaaa part1.mp4" -cat "001 aaaa part2.mp4" "001 aaaa.mp4"合并它们

  4. 对所有零件文件重复此操作

我尝试用 fish/bash 来做到这一点,但失败了。最后,我使用了一些其他的编程语言来完成,但我仍然想知道是否可以用 fish 或 bash.

来完成

假设文件名正常(名称中没有额外的“.”,当前目录中没有奇怪的其他文件...):

for prefix in `ls | sed -e 's/ .*//' | uniq -d` ; do
  first=`ls $prefix* | head -1`
  name=`echo "$first" | sed -e 's/ part.+\.//'
  cmd="mp4box -add '$first'"
  for rest in `ls $prefix* | tail +2` ; do
    cmd="$cmd -cat $rest"
  done
  $cmd $name
done

未经测试。适当地测试和裁剪。

编辑:将此视为 bash 伪代码,请参阅评论以了解明显的问题。

在 fish 中,我不会遍历文件而是遍历数字,所以像

for num in (printf '%03d\n' (seq 1 8)) # Pad the numbers to the given length - assuming they are all three digits long
    set -l files $num* # This will generate a list with as many elements as there are matching files, so if there's no file, there's zero elements
    count $files >/dev/null; or continue # Skip numbers that aren't used
    set -l name (string replace -r 'part.' '' -- $files[1]) # Or sed if you are using fish < 2.3
    if test (count $files) -gt 1
        mp4box -add $files[1] -cat $files[2..-1] $name # Assuming all but the last argument after cat are taken to be files to concatenate, otherwise it's a little more complicated
    else
        mv $files $name # If there's one file, just rename it
    end
end

以下脚本可能对您有所帮助。我写了一般模式,你需要修改它才能使用 mp4box:

#!/bin/bash

# Iterate over all file name prefixes which represent a split file.
for i in 001 003 004; do

    # An array to hold paths of split files.
    arr=()

    # Find all files in present-working-directory ( PWD ) which have the current prefix in their file name,
    # and add them to the split files array sorted by 'part' number.
    while read -rd '' f; do
        # The index of the entry. Parts must be sorted.
        num=${f##* part}
        num=${num%.*}
        # Put "$f" in the correct array cell, according to its part index.
        arr[10#$num]=$f
    done < <(find . -type f -name "$i *.mp4" -print0)

    # We're making arr not sprase, so we'll know for sure that arr[0] is not empty,
    # from which we'll derive the destination file name.
    arr=("${arr[@]}")

    # If we have more than a single split file in the split files array, then
    if (( ${#arr[@]} > 1 )); then
        # Create a new name for the destination mp4.
        new_name="${arr[0]% *}.mp4"

        # And...
        printf '%s %s\n' "Use mp4box to combine the following files to a new file named" "$new_name"
        printf '%s\n' "${arr[@]}"
    fi
done

我们来测试一下(脚本名称是sof):

$ ./sof
Use mp4box to combine the following files to a new file named ./001 aaaa.mp4
./001 aaaa part1.mp4
./001 aaaa part2.mp4
Use mp4box to combine the following files to a new file named ./003 cccc.mp4
./003 cccc part1.mp4
./003 cccc part2.mp4
Use mp4box to combine the following files to a new file named ./004 dddd.mp4
./004 dddd part1.mp4
./004 dddd part2.mp4

如果这确实是您要实现的目标,可以像这样创建 mp4box 的正确参数(当然是在 if (( ${#arr[@]} > 1 )); then 块内):

# Create arguments to mp4box's -add option (i.e. file1 -cat file2 -cat file3 ...)
mp4args=("${arr[0]}"); for entry in "${arr[@]:1}"; do mp4args+=(-cat "$entry"); done
# Execute mp4box for current split files:
mp4box -add "${mp4args[@]}" "$new_name"
# Empty mp4args array here, or at the begining of the `for` loop.
mp4args=()

让我们在 mp4box 之前放一个 echo 来确定我们将要执行的内容,并对其进行测试:

$ ./sof
Use mp4box to combine the following files to a new file named ./001 aaaa.mp4
./001 aaaa part1.mp4
./001 aaaa part2.mp4
===
mp4box -add ./001 aaaa part1.mp4 -cat ./001 aaaa part2.mp4 ./001 aaaa.mp4
===

Use mp4box to combine the following files to a new file named ./003 cccc.mp4
./003 cccc part1.mp4
./003 cccc part2.mp4
===
mp4box -add ./003 cccc part1.mp4 -cat ./003 cccc part2.mp4 ./003 cccc.mp4
===

Use mp4box to combine the following files to a new file named ./004 dddd.mp4
./004 dddd part1.mp4
./004 dddd part2.mp4
===
mp4box -add ./004 dddd part1.mp4 -cat ./004 dddd part2.mp4 ./004 dddd.mp4
===