bash - 类似于 Dropbox 的 bash 脚本
问题描述
我正在尝试编写一个 bash 脚本来同步两个类似于 Dropbox 服务所做的文件夹,即删除最新文件夹中不存在的文件/文件夹并复制最新的东西。但是我不确定如何处理路径/文件名。因此,我向您征求一些建议,以及可能改进脚本的问题。这是代码:
#!/bin/bash
out=/dev/stdout
#outerr=/dev/stderr
outerr="test.txt"
if [ $# -lt 2 ]
then
echo "" >> $out
echo " Not enough arguments passed to the script, try again next time." >> $out
echo "" >> $out
echo " Usage: " >> $out
echo " bash files_sync.sh [--t] SOURCE_NEW DESTINATION_OLD " >> $out
echo "" >> $out
echo " Options: --t just prints the modifications, without taking any action" >> $out
echo "" >> $out
exit 1
fi
test=0
if [ $# -eq 2 ]
then
source=$(printf '%s\n' "$1" | sed 's/\///')
dest=$(printf '%s\n' "$2")
#dest=$(printf '%s\n' "$2" | sed 's/\///')
fi
if [ $# -eq 3 ]
then
if [ $1 == "--t" ]
then
echo "" >> $out
echo " Printing the suggested actions..." >> $out
test=1
source=$(printf '%s\n' "$2" | sed 's/\///')
dest=$(printf '%s\n' "$3")
else
echo " What's your game, dude? Exiting. " >> $out
exit 20
fi
#source=$(printf '%s\n' "$2" | sed 's/\///')
#dest=$(printf '%s\n' "$3" | sed 's/\///')
fi
if [ ! -d $source ]; then
echo "" >> $out
echo " Path $source does not exists or not a folder, exiting. " >> $out
echo "" >> $out
exit 2
else
echo "" >> $out
echo " (SOURCE) UP-TO-DATE FOLDER: $source" >> $out
echo "" >> $out
fi
if [ ! -d $dest ]; then
echo " Path $dest does not exists or not a folder, exiting. " >> $out
echo "" >> $out
exit 3
else
echo " (DESTINATION) TO BE SYNCHED FOLDER: $dest" >> $out
echo "" >> $out
fi
echo -n " Do you REALLY want to proceed? [y/N] : " >> $out
read choice
echo "" >> $out
if [ $choice == "n" ]; then
echo " Brilliant, bye!" >> $out
echo "" >> $out
exit 4
fi
if [ $choice == "y" ]
then
echo " Awesome! Let's get started ...."
#FIRST CHECK IF DEST FILES ARE IN SOURCE. IF NOT DELETING THEM
list1=`find $source -mindepth 1 -name "*" | sed 's/'"$source"'\///'`
list2=`find $dest -mindepth 1 -name "*"`
#list2=`find $dest -mindepth 1 -name "*" | sed 's/'"$dest"'\///'`
outdest="testdest.txt"
echo " Writing DEST files into $outdest file" >> $out
> $outdest
for filedest in $list2
do
echo $filedest >> $outdest
done
outsrc="testsrc.txt"
echo " Writing SRC files into $outsrc file" >> $out
> $outsrc
for filesrc in $list1
do
echo $filesrc >> $outsrc
done
outerr="testerr.txt"
>$outerr
echo " Writing operations into $outerr file" >> $out
for filedest in $list2
do
#if [ ! -d $filedest ]
#then
check="true"
for filesrc in $list1
do
#if [ ! -d $filesrc ]
#then
if [ $filedest == $filesrc ]
then
check="false"
break
fi
#fi
done
if [ $check == "true" ]
then
if [ $test -eq 1 ]
then
echo "rm $dest/$filedest" >> $outerr
else
echo " ! "; rm $dest/$filedest
fi
fi
#fi
done
#THEN CHECK IF FILE IN SOURCE IS IN DEST. IF NOT COPY ELSE CHECK IF IT IS NEWER
for filesrc in $list1
do
check="true"
for filedest in $list2
do
if [ $filedest == $filesrc ]
then
check="false"
if [ $source/$filesrc -nt $dest/$filedest ]
then
if [ -d $source/$filesrc ]
then
echo " folder here, do nothing" >> $outerr
else
if [ $test -eq 1 ]
then
echo "cp $source/$filesrc $dest/$filedest" >> $outerr
else
echo " ! "; cp $source/$filesrc $dest/$filedest
fi
fi
fi
#break
fi
done
if [ $check == "true" ]
then
if [ $test -eq 1 ]
then
#echo -n " +++ $source/$filesrc not existing in DEST : " >> $outerr
if [ -d $source/$filesrc ]
then
echo "mkdir $dest/$filesrc" >> $outerr
else
echo "mv $source/$filesrc $dest/ " >> $outerr
fi
else
if [ -d $source/$filesrc ]
then
echo " ! "; mkdir $source/$filesrc
else
echo " ! "; mv $source/$filesrc $dest/$filedest
fi
fi
fi
done
echo "" >> $out
echo ' --------- Sync COMPLETE ! -------------' >> $out
echo "" >> $out
else
echo "" >> $out
echo ' Not getting the answer. Exiting' >> $out
echo "" >> $out
fi
先感谢您,
阿尔多
解决方案
我已将脚本的内容缩减为一个循环。我想我正在做你想做的事。我改变了所有:
if [ something ]
then
到if [something ]; then
. 我认为它使所有缩进更容易阅读。我删除了预删除逻辑,并在所有复制完成后添加了一个循环。所以整个循环消失了:
for filedest in $list2
do
#if [ ! -d $filedest ]
#then
check="true"
for filesrc in $list1
do
#if [ ! -d $filesrc ]
#then
if [ $filedest == $filesrc ]
then
check="false"
break
fi
#fi
done
if [ $check == "true" ]
then
if [ $test -eq 1 ]
then
echo "rm $dest/$filedest" >> $outerr
else
echo " ! "; rm $dest/$filedest #logic error here.
fi
fi
#fi
done
另一个重大变化是所有引用和内部字段分隔符(IFS)的更改。(编辑:我将 using 更改find
为 glob ( *
),因此无需更改 IFS)这些都是确保您可以处理包含空格的文件名所必需的。这是我最终得到的结果:
#!/bin/bash
out=/dev/stdout
#outerr=/dev/stderr
#outerr="test.txt"
function sync_dir
{
#This is our current subdirectory of $source and $dest.
#Note: In the original call we do not pass an argument so $subdirs is blank.
#This means that "$source/$subdirs"* = "$source/"* for the original call.
local subdirs="$1"
#By removing these find calls, we can just use * and avoid making changes to the IFS
#local list1=`find "$source/$subdirs" -mindepth 1 -maxdepth 1 -name "*"`
#local list2=`find "$dest/$subdirs" -mindepth 1 -maxdepth 1 -name "*"`
#No need for find. Let * do all the work.
for filesrc in "$source/$subdirs"*; do
echo "$filesrc" >> $outsrc
#remove path from $filesrc
local filewithoutdir="$(basename "$filesrc")"
#if "$filesrc" is a directory
if [ -d "$filesrc" ]; then
#check to see if the destination is a directory. Make it if not...
if [ ! -d "$dest/$subdirs$filewithoutdir" ]; then
if [ $test -eq 1 ]; then
echo "mkdir \"$dest/$subdirs$filewithoutdir\"" >> $outerr
else
echo " ! "; mkdir "$dest/$subdirs$filewithoutdir" 2>> $outerr
fi
fi
#recursive call if we are dealing with a directory.
sync_dir "$subdirs$filewithoutdir/"
#else if not a file OR "$filesrc" is newer than the destination, copy it.
elif [ ! -f "$dest/$subdirs$filewithoutdir" -o "$filesrc" -nt "$dest/$subdirs$filewithoutdir" ]; then
if [ $test -eq 1 ]; then
echo "cp \"$filesrc\" \"$dest/$subdirs$filewithoutdir\"" >> $outerr
else
echo " ! "; cp "$filesrc" "$dest/$subdirs$filewithoutdir" 2>> $outerr
fi
fi
done
#Here we loop throught the destination directory
for filedest in "$dest/$subdirs"*; do
echo "$filedest" >> $outdest
local filewithoutdir="$(basename "$filedest")"
#If the files do not exist in the source directory, delete them.
if [ ! -e "$source/$subdirs$filewithoutdir" ]; then
if [ $test -eq 1 ]; then
echo "rm -f \"$filedest\" >/dev/null 2>&1" >> $outerr
else
#We allow this to fail and silence it because it may try to delete a directory.
echo " ! "; rm -f "$filedest" >/dev/null 2>&1
fi
fi
#if you want to remove files and directories use this, but this can be dangerous.
#if [ ! -e $source/$filewithoutdir ]; then
# if [ $test -eq 1 ]; then
# echo "rm -rf \"$filedest\" >/dev/null 2>&1" >> $outerr
# else
# echo " ! "; rm -rf "$filedest" 2>> $outerr
# fi
# rm -rf "$filedest" >/dev/null 2>&1
#fi
done
}
if [ $# -lt 2 ]; then
echo "" >> $out
echo " Not enough arguments passed to the script, try again next time." >> $out
echo "" >> $out
echo " Usage: " >> $out
echo " bash files_sync.sh [--t] SOURCE_NEW DESTINATION_OLD " >> $out
echo "" >> $out
echo " Options: --t just prints the modifications, without taking any action" >> $out
echo "" >> $out
exit 1
fi
test=0
if [ $# -eq 2 ]; then
#This will cause an issue when processing full path like /home/user/bla
#source=$(printf '%s\n' "$1" | sed 's/\///')
#dest=$(printf '%s\n' "$2")
#dest=$(printf '%s\n' "$2" | sed 's/\///')
source="$1"
dest="$2"
fi
if [ $# -eq 3 ]; then
if [ $1 == "--t" ]; then
echo "" >> $out
echo " Printing the suggested actions..." >> $out
test=1
source="$2"
dest="$3"
else
echo " What's your game, dude? Exiting. " >> $out
exit 20
fi
fi
if [ ! -d $source ]; then
echo "" >> $out
echo " Path $source does not exists or not a folder, exiting. " >> $out
echo "" >> $out
exit 2
else
echo "" >> $out
echo " (SOURCE) UP-TO-DATE FOLDER: $source" >> $out
echo "" >> $out
fi
if [ ! -d $dest ]; then
echo " Path $dest does not exists or not a folder, exiting. " >> $out
echo "" >> $out
exit 3
else
echo " (DESTINATION) TO BE SYNCHED FOLDER: $dest" >> $out
echo "" >> $out
fi
echo -n " Do you REALLY want to proceed? [y/N] : " >> $out
read choice
echo "" >> $out
if [ $choice == "n" ]; then
echo " Brilliant, bye!" >> $out
echo "" >> $out
exit 4
fi
if [ $choice == "y" ]; then
echo " Awesome! Let's get started ...."
outdest="testdest.txt"
echo " Writing DEST files into $outdest file" >> $out
> $outdest
outsrc="testsrc.txt"
echo " Writing SRC files into $outsrc file" >> $out
> $outsrc
outerr="testerr.txt"
echo " Writing operations into $outerr file" >> $out
>$outerr
#function call
sync_dir
#If we are not in test mode and errors have occured, they will be written to $outerr.
#This test (-s) checks to see that $outerr is not zero length (implies -e).
if [ ! $test -eq 1 -a -s $outerr ]; then
echo "WARNING: Errors have occured during the running of this script. See $PWD/$outerr for details" >> $out
fi
echo "" >> $out
echo ' --------- Sync COMPLETE ! -------------' >> $out
echo "" >> $out
else
echo "" >> $out
echo ' Not getting the answer. Exiting' >> $out
echo "" >> $out
fi
目前,这不会复制源中目录的内容。如果那是你想要的,你可以把这个东西的肉放在一个函数中。然后cd
进入目标目录并递归调用该函数以复制子目录的内容。
编辑:现在实现了递归版本。我的早期版本实际上是错误的。我将 -mindepth 误读为 -maxdepth。-maxdepth 会阻止我们读取第一个目录。编辑后的代码实际上将适用于子目录和完整路径目录。
您还会注意到我在,和电话2>> $outerr
的末尾添加了。这将捕获任何输出到 stderr 并将其写入. 请记住,所有这些命令都可能失败,因此至少捕获错误很重要。在测试模式下,您正在打印所有将用于. 当您不在测试模式时,您根本没有使用它,所以我用处理过程中可能发生的任何错误填充它。最后,我们检查是否报告了任何错误并发布一个小警告。如果您想安全起见,请更改为强制退出任何错误。cp
rm
mkdir
$outerr
$outerr
-s $outerr
2>> $outerr
2>> $outerr || exit 2
推荐阅读
- angular - 有没有办法像我们在codeigniter中那样从角度7中的url获取片段
- python - 如何将python变量插入sql表
- hadoop - 应用程序 application_1576595769387_0004 失败 2 次,因为 AM Container for appattempt_1576595769387_0004_000002 以 exitCode:1 退出
- c++ - 为什么在 C++ 宏扩展为注释时出现“预期声明”错误?
- python - tesseract.exe 未安装或不在您的路径中
- c++ - 未定义的 new 运算符如何导致 C++ 中的未定义行为?
- cassandra - 我应该在 Cassandra-Driver 4.x 中为 localDataCenter 使用什么
- node.js - 如何使用指示性添加唯一规则?
- while-loop - 尝试在 db2 中使用插入执行 while 循环时出现问题
- notepad++ - 在 Notepad++ 中自定义上下文菜单