我正在编写一个在 Synology DS 1019+ 上运行的脚本,该脚本获取某个目录中的所有子文件夹,在另一个目录中创建子文件夹,然后在新创建的子文件夹中创建子文件夹中所有 .mkv 文件的硬链接另一个位置。但是,如果子文件夹已存在于第二个位置,那么我希望它只创建硬链接。
由于我的文件的性质,对于此脚本将用于的每个实例,文件夹结构和所述文件夹中的文件将有所不同。有没有办法让这个脚本循环,以便每个循环获取位置 A 中的下一个文件夹并执行每个子文件夹的 mkdir 和硬链接任务(到所述子文件夹中的文件)?
这是我目前拥有的:
#! /bin/bash
echo "Enter Movie Collection:"
read MOVIE_DIR
echo "Enter Bonus Feature Disc: "
read BONUS_DIR
cd "/volume1/Plex/Movies/$MOVIE_DIR/"
for dir in */.; do
if [[ ! -e "$dir"/*/ ]]; then
mkdir "$dir"/*/
fi
if [[ ! -d "$dir"/*/ ]]; then
ln /volume1/Plex/"Bonus Feature Discs"/$BONUS_DIR/*/*.mkv -t ./"$dir"/*/.
fi
done
我对循环一点也不熟练,尤其是嵌套循环,所以我不知道从哪里开始解决这个问题。目前,我没有将位置 A 中的文件夹复制到位置 B 中(如果尚不存在),也没有将位置 A 中的文件夹内的文件硬链接,而是在位置 B 的主目录(例如,在我的测试中,不是“TEST 1”获取其中包含硬链接 TEST.mkv 的“featurette”文件夹,而是在“TEST 1”中获取空的垃圾数据文件夹。
我尝试使用基名和目录名,但我还没有找到一种方法让它循环,以便它循环遍历目录中的每个文件夹。我也尝试过使用cd /the/directory/path/ "${PWD##*/}"
编辑:在发布此内容后的几个小时内,我确实找到了解决方案。我上面的代码在逻辑上根本不合理,因为有太多不具体的区域,这意味着什么也不会发生。我最终不得不从头开始。这是我最终得到的代码,该代码确实完成了我需要它完成的工作。它可能不是执行此操作的最优雅的方法,但根据我运行的测试,它确实工作得足够好。
#! /bin/bash
#Ask the user to input the directories of both the bonus disc's files and where the movies are located that the bonus disc's contents will be needed.
echo "Enter the name of the Movie Collection and press [ENTER]: "
read MOVIE_DIR
echo "Enter the name of the Bonus Feature Disc and press [ENTER]: "
read BONUS_DIR
#This goes to the location of the bonus disc specified by the end user. I believe this part is necessary for creating the text document below, but it might not be.
cd "/volume1/Plex/Bonus Feature Discs/$BONUS_DIR/" || return
#This creates a text document that has each directory within the specified Bonus Disc directory as a separate line
ls -d -- */ >> /volume1/Plex/"Bonus Feature Discs"/output.txt
echo ls -d -- */
#This goes to the movie directory specified by the end user. This cd is definitely required
cd "/volume1/Plex/Movies/$MOVIE_DIR/" || return
#the for loop loops through every movie that resides in the movie collection folder
for dir in *; do
#this while loop reads the text document and will use each line from the document as a variable.
while IFS=' ' read -r line; do
name="$line"
#A directory with the name of the line of the text document will be created in each movies directory only if it doesn't already exist
mkdir -p "$dir/$name"
#this will create the hard links to every single video that resides in the folders within the bonus features disc into the corresponding folders for each movie
if [[ ! -e "/volume1/Plex/Movies/$MOVIE_DIR/$name" ]]; then
ln "/volume1/Plex/Bonus Feature Discs/$BONUS_DIR/$name"*.mkv -t ./"$dir/$name"
fi
done < "/volume1/Plex/Bonus Feature Discs/output.txt"
done
echo "Linking Completed"
echo $dir
#finally, once all the work is complete, the script will delete the text document that is no longer needed
rm "/volume1/Plex/Bonus Feature Discs/output.txt"
答案1
使用rsync
,并假设您希望将目录.mkv
中或source
目录下的所有文件硬链接到目录中target
,包括创建所有目录(即使它们没有.mkv
文件)。
rsync --archive --link-dest="$PWD/source" \
--include='*/' \
--include='*.mkv' \
--exclude='*' \
source/ target
--archive
( )选项-a
可以rsync
保留文件元数据并触发目录的递归复制。该--link-dest
选项提供了rsync
一个目录,可以将现有文件从该目录硬链接到target
.这些--include
选项选择目录和.mkv
文件,而最后一个--exclude
选项则忽略尚未使用 选定的所有内容--include
。
要删除 中可能生成的空目录target
,可以使用find
如下命令:
find target -type d -empty -delete
...假设您find
实施了-empty
测试和-delete
操作。
例子:
source
|-- dir1
| |-- file1.mkv
| |-- file1.txt
| |-- file2.mkv
| `-- file2.txt
|-- dir2
| |-- file1.mkv
| |-- file1.txt
| |-- file2.mkv
| `-- file2.txt
|-- dir3
| |-- file1.mkv
| |-- file1.txt
| |-- file2.mkv
| `-- file2.txt
`-- dir4
`-- file1.doc
上面的命令rsync
运行后target
变成...
target
|-- dir1
| |-- file1.mkv
| `-- file2.mkv
|-- dir2
| |-- file1.mkv
| `-- file2.mkv
|-- dir3
| |-- file1.mkv
| `-- file2.mkv
`-- dir4
只是显示文件是硬链接的(相同的 inode 编号,链接计数为 2):
$ ls -l -i source/dir1/file1.mkv target/dir1/file1.mkv
3118217 -rw-r--r-- 2 kk kk 0 Apr 10 17:03 source/dir1/file1.mkv
3118217 -rw-r--r-- 2 kk kk 0 Apr 10 17:03 target/dir1/file1.mkv
删除空目录:
$ find target -type d -empty -delete
$ tree target
target
|-- dir1
| |-- file1.mkv
| `-- file2.mkv
|-- dir2
| |-- file1.mkv
| `-- file2.mkv
`-- dir3
|-- file1.mkv
`-- file2.mkv
答案2
我假设您以某种方式为两个变量dirA
和赋值dirB
(描述中的“位置 A”和“第二位置”)。
您循环遍历dirA
.
每次 中的文件dirA
是一个目录时,您都会在 上创建另一个同名的目录dirB
。如果新目录已存在,该--parent
开关可防止mkdir
引发错误,从而无需检查新目录是否存在。
内部if
检查是否存在 .mkv 文件,如果没有则跳过该目录。
该脚本适用于单个目录级别:如果目录中有目录,则不会查看这些目录。
#! /bin/bash
dirA=...
dirB=...
cd "$dirA"
for dir in *; do
if [ -d "$dir" ]; then
names=$(find "$dir" -maxdepth 1 -name '*.mkv')
if [ "$names" ]; then
mkdir --parent "$dirB/$dir"
ln "$dir"/*.mkv "$dirB/$dir"
fi
fi
done