这个问题回答了我如何使用外部计算机创建RPi备份的问题。

我想知道是否可以创建当前正在使用的SD卡的备份映像,并将其复制到USB存储设备上的文件中。这可能吗?如果不是,是否有任何方法可以创建RPi的备份而不涉及另一台计算机?

评论

可以,但是跳过/ tmp,/ run,/ proc,/ sys,/ dev和/ mnt。您无需创建映像,您需要可以创建或更新映像的备份。因此,请勿使用dd,而应使用rsync。

@goldilocks如果您可以将此注释充实为更完整的答案,并解释您要考虑的备份和还原过程,我会喜欢的。

完成-很抱歉,我花了几天时间。

如果目标卷足够大,则对于“新”副本而言,以只读方式重新挂载文件系统并以适当的块大小进行dd副本可能最快。将文件逐个文件复制到Flash / SD介质可能是个坏主意。

#1 楼

这是使用rsync在Pi上进行备份的简介。创建初始备份后,以这种方式保持最新状态要比不断撕裂整个映像快得多。您可以在本地硬盘驱动器上或通过网络执行此操作。
您实际上不希望将正在运行的系统的完整副本作为备份,因为表面上文件系统中的某些内容仅在运行时存在。将其包含在备份中,然后在以后使用它来重新创建映像可能会给您带来问题。
还有其他一些例外。 rsync可以接受要排除的(全局)模式列表,并且可以从文件中读取这些模式,因此让我们首先了解一下该文件中应包含的内容。请注意,条目的形式为/directory/*而不是/directory。这是因为我们希望它们存在,但我们不想在其中复制任何内容。
/proc/*
/sys/*

这些在磁盘上实际上并不存在。它们是内核的接口,可在内存中创建和维护它们。如果您复制这些文件,然后将它们复制回系统并进行引导,则(至多)它是毫无意义的,因为内核将这些文件用作接口的安装点[如果要查看在安装文件系统分区时发生了什么在其中有数据的目录上,尝试。它可以工作并且不会造成任何伤害,但是现在无法访问目录中的内容。]
请注意,必须存在/sys/proc挂载点,这一点至关重要。但它们不应包含任何内容。 Next:
/dev/*

dev目录与procsys并不完全相同,但就我们的目的而言。如果您认为应该保存此设置,以便备份中可以有相同的设备节点或其他内容,那是错误的。不要打扰不要复制dev。很久很久以前,Linux就是以这种方式工作的,但是现在已经不行了。
/boot/*

这是大多数(也许全部)Pi特定发行版(例如Raspbian)的一种特殊情况。实际上,它是第一个vfat分区的安装点。我们将分别处理。无论您做什么,都不要在这里添加它,因为它再次是挂载点。 /run也许也可以(这可以节省一些SD卡操作),但是在任何情况下,顾名思义,这些都不是存储持久数据的地方。使用它们的应用程序希望它们会在每次引导时被删除。自动挂载倾向于使用后者),因为如果您不排除这些设备在文件系统中的位置,则会创建一个循环,将驱动器的内容备份到自身,直到驱动器空间不足为止。我认为/tmp可能足够聪明,可以发现一些愚蠢的东西,但要避免测试前提。 -例如“ pi_backup”。您可以通过/mnt(请参见下文)或使用网络安装的文件系统来备用备份到远程位置,但这可能是第一次需要一段时间。是/media,以进行实际备份:
/tmp/*
/run/*

请注意,rsync后面有斜杠。
这将花费一些时间并产生大量输出(如果您想在日志中检查它,请附加ssh)。 /rsync-exclude.txt第一次没有意义,但是为了保持备份更新,请使用它。这样可以确保您以后在Pi上删除的内容也可以从备份中删除。 /mnt/usbhd将递归设置为目录,并确保所有文件属性都匹配。 pi_backup/用于保留硬链接2,> rsync.log用于表示冗长,这就是为什么您会得到一些输出的原因(否则,--delete很安静)。有关更多信息,请参见a
有一个快捷方式,您可以跳过-H文件。如果您确定所有不想复制的内容(v等)都位于单独的文件系统上,则可以使用:
/mnt/*
/media/*

rsync已插入。这是man rsync的缩写,它告诉--exclude-from不要越过文件系统边界。我个人更喜欢/tmp,但是在例如默认Raspbian上,-x可以正常工作。如果您想小心一点,可以同时使用这两种方法:D
那不是一个完整的备份。如果您没有在--one-file-system中放任何东西就足够了,并且可以很好地使用备份通过将卡插入计算机并运行来恢复系统:
rsync -aHv --delete --exclude-from=/rsync-exclude.txt / /mnt/usbhd/pi_backup/

您也可以使用一张上面有新图像的卡(假定它与基本图像相同),但是如果必须创建图像(因为随后将要覆盖其中的大部分图像)则效率较低。您还可以通过USB适配器连接另一张SD卡,上面带有这样的图像,并使用上述方法维护一张重复的卡。 rsync,您也想备份它(非常简单-它没有太多内容)。只需单独进行操作,然后在还原时,这些内容将进入第一个分区。
如果要创建空白的Raspbian样式图像,然后可以将其备份到其中,请参见此处。您可以使用类似的方法来创建一个空的Raspbian样式卡-而不是处理一个--exclude-from文件,而是要处理一个真实的设备(例如--one-file-system),这意味着您要做的就是使用以下方法创建分区表: -x,然后使用boot格式化/boot/boot/config.txt(或其他格式)。
但是复制整个图像更容易!为什么要为此烦恼?
不那么难;我在10分钟内恢复了一张空白卡(按照最后一个链接的格式格式化)。是的,仅在整个过程中使用.img会更简单(如果您发现令人困惑的单词之类的东西……),但是每次您要更新备份都需要花费相当长的时间,因为您每次必须进行100%的备份。使用/dev/sdb,一旦存在备份,更新速度就快得多,因此您可以将其设置为每天通过cron轻松完成。甚至通过网络。每六个小时。

fdisk通过/dev/sdb1

这里有一个例子:例如,sdb2和“ ssh选项”就是您通常使用的(如果有的话)。为了进行系统备份,必须通过mkfs具有root访问权限(在dd中设置rsync并重新启动服务器)。

1您应保留此文件。您可以在以rsyncssh开头的行中添加注释。这可能包括实际的-av --delete --exclude-from=/rsync-exclude.txt命令,以后可以复制粘贴该命令,这样您就不必每次都记住它。
2感谢Kris指出ssh不会自动执行此操作。

评论


金发姑娘。看起来rsync非常有用。有没有机会将其滚动成脚本?

–极权主义者
13年8月27日在7:47

为什么不手动排​​除所有安装点,为什么不mkdir / tmp / backupable && mount --bind / / tmp / backupable并使rsync呢?这还具有备份存储在被安装在其中的某些东西“遮盖”的地方的所有数据的优点。

–n.st
2014年12月18日下午5:11

@ n.st好主意(哈哈)!尽管我仍然认为使用--exclude-from是一个更好的主意,但我已经将建议编辑到了问题中。如果有时间,您可以将其写成一个单独的答案,如果有我的投票,我可以参考。这个答案已经很久了。

– goldilocks♦
2014年12月18日在21:09

@IgorGanapolsky的目的是不创建图像(请阅读“但是复制整个图像更容易!为什么要麻烦这个?”部分)。创建后,除了更易于维护之外,此方法通常更灵活。如果要以后使用它来创建.img,可以;这将有助于解释它们的结构和创建方式。

– goldilocks♦
17年1月25日在15:23



请参阅开头的段落,“那不是一个完整的备份...”。相反,这基本上是完全相同的事情。这可能会帮助人们经常混淆的一些概念。

– goldilocks♦
17年1月25日在15:38



#2 楼

Raspberry社区的成员编写的工作脚本。

您可以随意使用和调整代码。它有充分的文档说明和自我解释。

 #!/bin/bash

# Setting up directories
SUBDIR=raspberrypi_backups
DIR=/hdd/$SUBDIR

echo "Starting RaspberryPI backup process!"

# First check if pv package is installed, if not, install it first
PACKAGESTATUS=`dpkg -s pv | grep Status`;

if [[ $PACKAGESTATUS == S* ]]
   then
      echo "Package 'pv' is installed."
   else
      echo "Package 'pv' is NOT installed."
      echo "Installing package 'pv'. Please wait..."
      apt-get -y install pv
fi

# Check if backup directory exists
if [ ! -d "$DIR" ];
   then
      echo "Backup directory $DIR doesn't exist, creating it now!"
      mkdir $DIR
fi

# Create a filename with datestamp for our current backup (without .img suffix)
OFILE="$DIR/backup_$(date +%Y%m%d_%H%M%S)"

# Create final filename, with suffix
OFILEFINAL=$OFILE.img

# First sync disks
sync; sync

# Shut down some services before starting backup process
echo "Stopping some services before backup."
service apache2 stop
service mysql stop
service cron stop

# Begin the backup process, should take about 1 hour from 8Gb SD card to HDD
echo "Backing up SD card to USB HDD."
echo "This will take some time depending on your SD card size and read performance. Please wait..."
SDSIZE=`blockdev --getsize64 /dev/mmcblk0`;
pv -tpreb /dev/mmcblk0 -s $SDSIZE | dd of=$OFILE bs=1M conv=sync,noerror iflag=fullblock

# Wait for DD to finish and catch result
RESULT=$?

# Start services again that where shutdown before backup process
echo "Start the stopped services again."
service apache2 start
service mysql start
service cron start

# If command has completed successfully, delete previous backups and exit
if [ $RESULT = 0 ];
   then
      echo "Successful backup, previous backup files will be deleted."
      rm -f $DIR/backup_*.tar.gz
      mv $OFILE $OFILEFINAL
      echo "Backup is being tarred. Please wait..."
      tar zcf $OFILEFINAL.tar.gz $OFILEFINAL
      rm -rf $OFILEFINAL
      echo "RaspberryPI backup process completed! FILE: $OFILEFINAL.tar.gz"
      exit 0
# Else remove attempted backup file
   else
      echo "Backup failed! Previous backup files untouched."
      echo "Please check there is sufficient space on the HDD."
      rm -f $OFILE
      echo "RaspberryPI backup process failed!"
      exit 1
fi
 


考虑在原始论坛中添加评论或发布您自己的版本以帮助成熟内容。稍加付出一点。

评论


如果在备份pi时文件系统(文件删除,添加了新文件)发生了变化该怎么办?

–keiki
2013年3月14日13:47



当它们使用rsync运行时,我备份了几个磁盘,而且我经常能够从这些文件备份中准确获得所需的磁盘。但是,通常,在挂载文件系统(*)时,不能完美地复制UNIX文件系统(每一位都正确无误)。挂载系统时制作的副本有时称为“脏副本”。可以采取几种措施来提高脏副本的质量(如上面的脚本所做的那样,关闭cron和mysql),但这并不是完美的。干杯! *-我错了,这取决于文件系统。

– Tai Viinikka
2013年3月14日14:28



您可以查看Debian建议的备份实用程序,并查看Pi是否具有它们的端口。 rsnapshot的声音令人振奋

– Piotr Kula
2013年3月14日14:29



@TaiViinikka您不需要完美的副本。您需要部分副本,可以(快速,轻松地)将其重新放置在原始基础图像上。 rsync是必经之路;明天我有时间的时候,我会添加一个答案。 rsnapshot也值得调查。

– goldilocks♦
13年3月14日在18:12

基于上面的ppumkins答案,我将'dd'脚本与原始线程中的最新注释进行了同步,并自己添加了一些小的改进。最终结果可以在这里找到:。请不要犹豫,添加改进并向我发送请求请求。

– AndersW
13年4月4日在21:36

#3 楼

我已将rsync上的@goldilocks答案改编为pi上的备份。
我将其备份到Pi上已安装的HDD上的ext4分区上。挂载目录(直到SD卡已满)。
如果未在rw模式下挂载HDD,则会产生大量错误消息。
这两个都不可取,因此我检查分区是否已装入rw模式在继续之前。

注意2015-03-03我修改了答案以准确复制硬链接。原始版本可以运行,但是将许多硬链接转换为文件。除了浪费空间之外,这还假定了硬链接已到位的许多用途。 (我当前的图像有869个链接,其中很多都是Raspbian本身。)

我的脚本如下。 (我的分区是PiData,安装在/mnt/PiData



 #!/bin/bash
# script to synchronise Pi files to backup
BACKUP_MOUNTED=$(mount | awk '/PiData/ {print }' | grep "rw")
if [ $BACKUP_MOUNTED ]; then
    echo $BACKUP_MOUNTED
    echo "Commencing Backup"
    rsync -avH --delete-during --delete-excluded --exclude-from=/usr/bin/rsync-exclude.txt / /mnt/PiData/PiBackup/
else
    echo "Backup drive not available or not writable"
fi
 


使用以下命令恢复(或更新其他Pi):-

 sudo rsync -avH /mnt/PiData/PiBackup/ /
 


我已经增强了功能rsync-exclude.txt消除不必要的文件。第一组是@goldilocks https://raspberrypi.stackexchange.com/users/5538/

记录的目录第二组是当我使用AFP(苹果文件协议)访问我的Pi时由OS X创建的文件和目录(这些通常在OS X上不可见,而在Raspbian上则不可见。无论如何,都无需备份。)即使

第三组是不需要备份的文件(当然也不必复制到其他Pi)。例如,fake-hwclock.data,RPi -监控器报告。您可能还会有其他人。

 /proc/*
/sys/*
/dev/*
/boot/*
/tmp/*
/run/*
/mnt/*

.Trashes
._.Trashes
.fseventsd
.Spotlight-V100
.DS_Store
.AppleDesktop
.AppleDB
Network Trash Folder
Temporary Items

.bash_history
/etc/fake-hwclock.data
/var/lib/rpimonitor/stat/
 


评论


有没有办法使输出为.img文件?

– IgorGanapolsky
17年1月25日在15:24

@IgorGanapolsky好吧,看到所有基本文件都存在(引导文件除外),这显然是可能的,但是如果要创建映像就可以。您应该在新帖子中提出任何新问题,而不要发表评论。

–地铁
17年1月26日在3:42

@Milliways为什么不应该使用“ sudo rsync ...”?某些文件可能无法同步?

– Smilia
18年11月4日在9:45

#4 楼

我的本地网络中运行着三台Pi,当它们启动并运行时,需要使用cron定期备份它们。这就是为什么我创建了一个能够创建dd,tar和rsync备份并还原它们的脚本的原因。我更喜欢使用rsync进行备份,但其他人更喜欢dd或tar。它已经被很多人使用。希望它对其他人也有用:-) raspibackup-Raspberry创建自己的备份

评论


不,很抱歉:要求用户运行(以root用户身份!)通过HTTP下载的脚本是不负责任的。请通过安全渠道分发此脚本。

–Clément
16年5月1日在20:38

我不认为这不是题外话,根本与否无关紧要。关键是应将软件分发到安全通道上,您的答案是鼓励不良的安全做法。

–Clément
16年5月7日在21:44

那将是向前迈出的一大步,是的:)

–Clément
16年5月9日,下午3:06

只是要注意,在这种情况下,通过HTTPS传递不会以任何方式增加任何安全性!您仍在从Internet下载并运行脚本。安全的过程是下载脚本(无关紧要的http / https),在编辑器中打开脚本并从上至下阅读,检查其是否奇怪和不安全。只有当您满意时才可以运行它。 Framp可能是我们所有人都知道的黑客,而在这种情况下,通过https传递信息只会让他微笑:)(顺便说一句,这不是Framp的指控!)

–朱利安·奈特(Julian Knight)
16年6月15日在15:02

我同意你的看法。这就是为什么有两种方法描述脚本的安装方式:1.使用installerScript 2.手动下载它,检查代码,然后手动安装

–抽筋
16年6月16日在17:40



#5 楼

这是我们用于此类目的的稳定工具:https://github.com/aktos-io/aktos-dcs-tools

此工具是从远程位置写入make ssh连接,make backup-rootmake mount-root首先,然后添加本地会话。因此,它支持本地备份,直接远程备份,代理远程备份。增量备份(仅传输差异),备份目录是独立的(只需选择要还原的目录/版本,任何目录都具有完整备份)。当然,您有版本(backup.last-0是最新版本)。您可以随时中断备份过程,然后再继续。

以下是针对您特定问题的说明:

 ssh to-your-raspberry
 cd /mnt/usb0/my-rpi-backups
 git clone https://github.com/ceremcem/aktos-dcs-tools backup-tools
 ln -s backup-tools/Makefile .

 ./backup-tools/configure # you do not need to make any settings for local sessions, just save the default 

 # just for the first time
 make set-local-session  # a flag file is created
 make init               # snapshots directory is created

 # anytime you want to back up
 make backup-root        # backup with rsync


编辑

现在添加了一个新目标:您可以使用一个命令从备份中创建物理SD卡:卡,使用此新创建的SD卡启动RaspberryPi。

#6 楼

这是一种完全不同的方法。您可以使用LVM(逻辑卷管理器)进行一致的备份。除了诸如轻松添加,扩展和减少存储或从快照将操作系统还原到较早状态之类的其他改进之外,您还可以进行备份。您不必担心备份期间动态更改的文件,将文件系统设置为只读,排除特定目录或其他内容。使用LVM,您只需创建一个快照,挂载该快照并使用您喜欢的方法备份它。您可以使用cp -a进行复制,使用rsync进行镜像,使用tar进行存档或使用dd进行图像制作。假设您已经在/mnt/usbhd/pi_backup/上安装了备份设备,则可以执行以下操作:如何做到这一点,您可以查看带有LVM的运行系统的轻松备份和快照。

评论


您如何将LVM方法与github上的Raspberry Pi备份脚本rpi-clone进行比较?

– Seamus
1月27日19:50

@Seamus LVM并不是一种脚本,它可以像我在此阅读的任何其他解决方案以及rpi-clone一样进行操作。它是管理存储设备的操作系统的一部分,甚至是内核的一部分,它只能被调用。除了轻松备份正在运行的系统外,您还可以对存储进行许多其他操作。例如。您可以在软件安装的不同阶段启动到不同的快照。您可以还原到快照并启动。它可以立即运行,无需摆弄备份文件并且恢复时间长。我用它来测试;总是全新安装。

– Ingo
1月27日21:48

@Seamus除此之外,您还可以通过管理存储来做很多其他事情,例如改善或缩小空间,添加其他物理卷(磁盘,SSD等)等等。

– Ingo
1月27日21:51

#7 楼

此答案没有新信息。我添加它仅是为了将这个问题及其答案与此处最近提出(并回答)的问题联系起来。我相信这两个问题及其答案都是相关的,对于那些寻求创建RPi系统备份解决方案的人来说,它们会很感兴趣。

编辑:注意-本主题中的新答案也解决了image-backup脚本。

最近的问与答提供了一个建议的备份解决方案的链接,该解决方案由RonR在此论坛中的Image File Utilities标题下发布。我已经分叉了一个image-backup的GitHub存储库;我正在努力通过编写的小脚本来使其保持最新状态。

引用作者的话:


image-backup创建正在运行的Raspbian系统到标准“原始”图像文件的备份,该文件可以写入到SD卡或具有Etcher,imageUSB等的USB设备。它还将对现有的备份映像文件执行增量更新。


#8 楼

我找到了一个可以安装映像的备份工具。

它还具有用于挂载和收缩映像的实用程序。

这可能对其他人有用

随附的文档非常简短,因此我需要注意以下内容:-


将实用程序提取到任何目录中,并使脚本可执行。您的Pi可以使用ext4/mnt(可以使用Pi支持的任何允许大文件的格式,例如exFAT或网络驱动器)。
对于首次运行,系统将提示您输入备用映像名称,例如/media

系统将提示您输入Image ROOT文件系统大小(以MB为单位),可以为0(表示最小),或者为空白(表示完全备份)。
在随后的运行中,输入备份路径要逐步更新的图像。

An example of the commands I used:-
# Mount USB
sudo mount /dev/sda1 /mnt/Image/
# Update backup
sudo image-utils/image-backup /mnt/Image/BusterBackup.img
# Mount backup
sudo image-utils/image-mount /mnt/Image/BusterBackup.img  MountedImages
When done, run:
sudo umount MountedImages; sudo losetup -d /dev/loop0
# Compress backup
sudo sh -c "gzip -9c /mnt/Image/BusterBackup.img  > Images/BusterBackup.img.gz"


我对原始图像做了一些修改(以复制安装点),以正确计算分区的偏移量和大小,并添加了一些注释。 />
#!/bin/bash
# Original https://raspberrypi.org/forums/viewtopic.php?p=1528736
# 2019-09-26    Modified to set size of boot sector

trap '{ stty sane; echo ""; errexit "Aborted"; }' SIGINT SIGTERM

ADDBLK=0

# Set BOOT_SIZE_MB to the Desired boot sector size (in MB) - should be multiple of 4MB
BOOT_SIZE_MB=256
BOOTSIZEM=$BOOT_SIZE_MB'M'

BOOTBEG=8192
BOOT_SIZE="$((BOOT_SIZE_MB * 1024 * 1024))"
ROUND_SIZE="$((4 * 1024 * 1024))"
# Ensure root sector starts on an Erase Block Boundary (4MB)
ROOTBEG=$(((BOOT_SIZE + ROUND_SIZE -1) / ROUND_SIZE * ROUND_SIZE / 512 + BOOTBEG))

MNTPATH="/tmp/img-backup-mnt"

ONEMB=$((1024 * 1024))

# create BOOT loop device
mkloop1()
{
  local INFO1=""
  local SIZE1=0
  local START1=0

  sync
  INFO1="$(sfdisk -d "${IMGFILE}")"
  START1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$||p')
  SIZE1=$(grep type=c <<< "${INFO1}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$||p')
  LOOP1="$(losetup -f --show -o $((${START1} * 512)) --sizelimit $((${SIZE1} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create BOOT loop device"
  fi
}

rmloop1()
{
  if [ "${LOOP1}" != "" ]; then
    sync
    losetup -d "${LOOP1}"
    LOOP1=""
 fi
}

# create ROOT loop device
mkloop2()
{
  local INFO2=""
  local SIZE2=0
  local START2=0

  sync
  INFO2="$(sfdisk -d "${IMGFILE}")"
  START2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*start=\s\+\([0-9]\+\).*$||p')
  SIZE2=$(grep type=83 <<< "${INFO2}" | sed -n 's|^.*size=\s\+\([0-9]\+\).*$||p')
  LOOP2="$(losetup -f --show -o $((${START2} * 512)) --sizelimit $((${SIZE2} * 512)) "${IMGFILE}")"
  if [ $? -ne 0 ]; then
    errexit "Unable to create ROOT loop device"
  fi
}

rmloop2()
{
  if [ "${LOOP2}" != "" ]; then
    sync
    losetup -d "${LOOP2}"
    LOOP2=""
  fi
}

# Mount Image partitions
mntimg()
{
  MNTED=TRUE
  if [ ! -d "${MNTPATH}/" ]; then
    mkdir "${MNTPATH}/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make ROOT partition mount point"
    fi
  fi
  mkloop2
  mount "${LOOP2}" "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image ROOT partition"
  fi
  if [ ! -d "${MNTPATH}/boot/" ]; then
    mkdir -p "${MNTPATH}/boot/"
    if [ $? -ne 0 ]; then
      errexit "Unable to make BOOT partition mount point"
    fi
  fi
  mkloop1
  mount "${LOOP1}" "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to mount image BOOT partition"
  fi
}

umntimg()
{
  umount "${MNTPATH}/boot/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image BOOT partition"
  fi
  rmloop1
  umount "${MNTPATH}/"
  if [ $? -ne 0 ]; then
    errexit "Unable to unmount image ROOT partition"
  fi
  rmloop2
  rm -r "${MNTPATH}/"
  MNTED=FALSE
}

errexit()
{
  echo ""
  echo ""
  echo ""
  if [ "${MNTED}" = "TRUE" ]; then
    umount "${MNTPATH}/boot/" &> /dev/null
    umount "${MNTPATH}/" &> /dev/null
    rm -rf "${MNTPATH}/" &> /dev/null
  fi
  rmloop1
  rmloop2
  exit 1
}

LOOP1=""
LOOP2=""
MNTED=FALSE

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

if [ $(id -u) -ne 0 ]; then
  errexit "q4312078q must be run as root user"
fi

PGMNAME="$(basename q4312078q)"
for PID in $(pidof -x -o %PPID "${PGMNAME}"); do
  if [ ${PID} -ne $$ ]; then
    errexit "${PGMNAME} is already running"
  fi
done

rsync --version &> /dev/null
if [ $? -ne 0 ]; then
  errexit "rsync not installed (run: apt-get install rsync)"
fi

if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
  SYSTEMD=1
elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
  SYSTEMD=0
else
  errexit "Unrecognized init system"
fi

if [ ${SYSTEMD} -eq 1 ]; then
  ROOT_PART="$(mount | sed -n 's|^/dev/\(.*\) on / .*||p')"
else
  if [ ! -h /dev/root ]; then
    errexit "/dev/root does not exist or is not a symlink"
  fi
  ROOT_PART="$(readlink /dev/root)"
fi

ROOT_TYPE=$(blkid "/dev/${ROOT_PART}" | sed -n 's|^.*TYPE="\(\S\+\)".*||p')

ROOT_DEV="${ROOT_PART:0:(${#ROOT_PART} - 1)}"
if [ "${ROOT_DEV}" = "mmcblk0p" ]; then
  ROOT_DEV="${ROOT_DEV:0:(${#ROOT_DEV} - 1)}"
fi

PTUUID="$(blkid "/dev/${ROOT_DEV}" | sed -n 's|^.*PTUUID="\(\S\+\)".*||p')"

DEVSIZE=$(blockdev --getsize64 "/dev/${ROOT_PART}")
BLKSIZE=$(blockdev --getbsz "/dev/${ROOT_PART}")
BLKCNT=$((${DEVSIZE} / ${BLKSIZE}))
INFO="$(df | grep /dev/root)"
DFKSIZE=$(awk '{print }' <<< "${INFO}")
DFKFREE=$(awk '{print }' <<< "${INFO}")
ROOTSIZE=$((${BLKCNT} * ${BLKSIZE}))
ROOTUSED=$(((${DFKSIZE} - ${DFKFREE}) * 1024))
IRFSMIN=$(((${ROOTUSED} + (${ADDBLK} * ${BLKSIZE}) + (${ONEMB} - 1)) / ${ONEMB}))
IRFSMAX=$(((${ROOTSIZE} + (${ONEMB} - 1)) / ${ONEMB}))

IMGFILE=""
if [ "${IMGFILE}" = "" ]; then
# Create Image file
  while :
  do
    echo ""
    read -r -e -i "${IMGFILE}" -p "Image file to create? " IMGFILE
    if [ "${IMGFILE}" = "" ]; then
      continue
    elif [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
      echo ""
      echo "${IMGFILE} does not begin with /mnt/ or /media/"
      continue
    fi
    if [ -d "${IMGFILE}" ]; then
      echo ""
      echo "${IMGFILE} is a directory"
    elif [ -f "${IMGFILE}" ]; then
      echo ""
      echo -n "${IMGFILE} already exists, Ok to delete (y/n)? "
      while read -r -n 1 -s answer; do
        if [[ "${answer}" = [yYnN] ]]; then
          echo "${answer}"
          if [[ "${answer}" = [yY] ]]; then
            break 2
          else
            break 1
          fi
        fi
      done
    else
      break
    fi
  done
  IRFSSIZE=""
  while :
  do
    echo ""
    read -r -e -i "${IRFSSIZE}" -p "Image ROOT filesystem size (MB) [${IRFSMAX}]? " IRFSSIZE
    if [ "${IRFSSIZE}" = "" ]; then
      IRFSSIZE=${IRFSMAX}
      break
    elif [ ${IRFSSIZE} -ge ${IRFSMIN} ]; then
      break
    else
      echo ""
      echo "Requested image ROOT filesystem size (${IRFSSIZE}) is too small (Minimum = ${IRFSMIN})"
      IRFSSIZE=${IRFSMIN}
    fi
  done
  echo ""
  echo -n "Create ${IMGFILE} [${IRFSSIZE} MB] (y/n)? "
  while read -r -n 1 -s answer; do
    if [[ "${answer}" = [yYnN] ]]; then
      echo "${answer}"
      if [[ "${answer}" = [yY] ]]; then
        break
      else
        errexit "Aborted"
      fi
    fi
  done
  if [ -f "${IMGFILE}" ]; then
    rm "${IMGFILE}"
    if [ $? -ne 0 ]; then
      errexit "Unable to delete existing image file"
    fi
  fi
  ROOTEND=$((${ROOTBEG} + ((${IRFSSIZE} * ${ONEMB}) / 512) - 1))
  truncate -s $(((${ROOTEND} + 1) * 512)) "${IMGFILE}"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image file"
  fi
# create image/partitions
  sync
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
n
p
1
${BOOTBEG}
+${BOOTSIZEM}
t
c
p
n
p
2
${ROOTBEG}
${ROOTEND}
p
w
EOF

  mkloop1
  mkloop2
  mkfs.vfat "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Unable to create image BOOT filesystem"
  fi
  dosfsck "${LOOP1}" > /dev/null
  if [ $? -ne 0 ]; then
    errexit "Image BOOT filesystem appears corrupted"
  fi
  if [ "${ROOT_TYPE}" = "f2fs" ]; then
    mkfs.f2fs "${LOOP2}" > /dev/null
  else
    mkfs.ext4 -q -b ${BLKSIZE} "${LOOP2}" > /dev/null
  fi
  if [ $? -ne 0 ]; then
    errexit "Unable to create image ROOT filesystem"
  fi
  rmloop2
  rmloop1
# Initialise image PARTUUID
  fdisk "${IMGFILE}" <<EOF > /dev/null
p
x
i
0x${PTUUID}
r
p
w
EOF
# Create empty directories in image root partition
  mntimg
  mkdir "${MNTPATH}/dev/" "${MNTPATH}/media/" "${MNTPATH}/mnt/" "${MNTPATH}/proc/" "${MNTPATH}/run/" "${MNTPATH}/sys/" "${MNTPATH}/tmp/"
  if [ $? -ne 0 ]; then
    errexit "Unable to create image directories"
  fi
  chmod a+rwxt "${MNTPATH}/tmp/"
  umntimg
  echo ""
  echo "Starting full backup (for incremental backups, run: q4312078q ${IMGFILE})"
# END of create image/partitions
else

# Check existing Image
  if [[ ! "${IMGFILE}" =~ ^/mnt/.*$ && ! "${IMGFILE}" =~ ^/media/.*$ ]]; then
    errexit "${IMGFILE} does not begin with /mnt/ or /media/"
  fi
  if [ -d "${IMGFILE}" ]; then
    errexit "${IMGFILE} is a directory"
  elif [ ! -f "${IMGFILE}" ]; then
    errexit "${IMGFILE} not found"
  fi
  echo "Starting incremental backup to ${IMGFILE}"
fi

# rsync root partition
mntimg
sync
rsync -aDH --partial --numeric-ids --delete --force --exclude "${MNTPATH}" --exclude '/dev' --exclude '/media' --exclude '/mnt/*/*' --exclude '/proc' --exclude '/run' --exclude '/sys' \
--exclude '/tmp' --exclude 'lost\+found' --exclude '/etc/udev/rules.d/70-persistent-net.rules' --exclude '/var/lib/asterisk/astdb.sqlite3-journal' / "${MNTPATH}/"
if [[ $? -ne 0 && $? -ne 24 ]]; then
  errexit "Unable to create backup"
fi
sync
umntimg


#9 楼

打开终端并输入'lsblk -f'。
这将显示所有已连接的存储设备。
然后输入'dd if = / dev / [sd卡的名称] bs = 1M'。
这会花费一些时间,因此您可能需要在后台运行它。
这与在Linux中备份SD卡的方法完全相同。

评论


这样可以备份所有内容,甚至可以备份不必要和不需要的文件。

– IgorGanapolsky
17年1月25日在15:27

这将导致备份不一致,因为在正在运行的系统上,备份期间发生了一些变化!

– Ingo
18年7月12日在9:36