使用find移动文件时保留目录结构


22

我创建了以下脚本,将定义的旧文件从源目录移动到目标目录。运行良好。

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

该脚本可以很好地移动文件,它也可以移动源子目录的文件,但不会在目标目录中创建子目录。我要在其中实现此附加功能。

举个例子

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

当我运行此脚本时,它还会将十六进制的文件移动到maxi目录中,但是我需要将相同的十六进制创建到maxi目录中,并将其文件移动到同一十六进制中。

Answers:


13

除了运行外mv /home/ketan/hex/foo /home/maxi,您需要根据.NET生成的路径来更改目标目录find。如果先切换到源目录并运行,这会更容易find .。现在,您只需将目标目录添加到产生的每个项目之前find。您需要在find … -exec命令中运行外壳程序以执行串联,并在必要时创建目标目录。

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

请注意,如果$destination包含特殊字符,为避免引用问题,您不能仅在shell脚本中替换它。您可以将其导出到环境中,使其到达内壳,也可以将其作为参数传递(这就是我所做的)。您可以通过对sh调用进行分组来节省执行时间:

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

另外,在zsh中,可以使用zmv函数,和.m glob限定词仅匹配正确日期范围内的常规文件。您需要传递一个替代mv函数,该函数在必要时首先创建目标目录。

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'

for x do,您在那里不见了;:)。另外,我不知道您想实现什么,$0但是我非常相信这将是sh:)。
米哈尔戈尔诺-

@MichałGórny for x; do在技​​术上不兼容POSIX(请检查语法),但是现代的shell允许for x dofor x; do;一些旧的伯恩炮弹没有发牢骚for x; do。在现代的shell上,with sh -c '…' arg0 arg1 arg2 arg3arg0成为$0arg1成为$1等。如果要$0成为sh,则需要编写sh -c '…' sh arg1 arg2 arg3。同样,某些Bourne外壳的行为有所不同,但是POSIX指定了这一点。
吉尔(Gilles)'所以

pushd似乎是一个更好的选择cd,因为它对当前环境的干扰较小。
jpmc26 '18

16

我知道find已指定,但这听起来像是工作rsync

我最经常使用以下内容:

rsync -axuv --delete-after --progress Source/ Target/

如果您只想移动特定文件类型的文件(例如),这是一个很好的示例:

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/

比其他解决方案更干净:)
纪尧姆(Guillaume)

2
我发现这--remove-source-files很有用,可以有效地移动文件而不是复制文件。这是rsync的绝佳用法。
汤姆(Tom),

3

您可以使用两个find(1)实例来完成此操作

总有cpio(1)

(cd "$soure" && find  | cpio -pdVmu "$destination")

检查cpio的参数。我给的


1
这将在所有带有空格的文件名上中断。
克里斯·

1
这将复制文件而不是移动它们。
吉尔(Gilles)'所以

可能不是一个完美的答案,但是它帮助我以某种或多或少的直接方式移动文件保留路径(我有足够的空间来复制文件然后删除)。Upvoted
AhHatem'14

3

它效率不高,但是如果您仅复制文件然后再删除,我认为代码更易于阅读和理解。

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

注意:@MV为自动操作发现的缺陷:

使用两个单独的操作是有风险的。如果在复制操作完成后某些文件的保留时间超过7天,则不会复制这些文件,但会通过删除操作将其删除。对于手动完成的操作,这可能不是问题,但是对于自动脚本,则可能导致数据丢失


2
使用两个单独的操作是有风险的。如果在复制操作完成后某些文件的保留时间超过7天,则不会复制这些文件,但会通过删除操作将其删除。对于手动完成的操作而言,这可能不是问题,但是对于自动化脚本,则可能导致数据丢失。
MV。

2
解决此缺陷的简单方法是运行一次find,将列表另存为文本文件,然后使用xargs两次进行复制,然后删除。
David M. Perlman,

1

您可以通过将返回的文件的绝对路径附加find到目标路径来做到这一点:

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'

克里斯现在将整个家搬到马克西
Ketan Patel

@ K.KPatel不,不是。它仅保留您的目录结构。
克里斯·

如果$destination包含特殊字符,这会中断,因为它会在内壳中进行扩展。也许是你的意思destination='\'"$destination"\''?那仍然断断续续'。此外,这还会创建/home/maxi/home/ketan/hex/foo而不是的文件/home/maxi/hex/foo
吉尔(Gilles)“所以,别再邪恶了”

0

更好(最快,而且不通过复制而不是移动来占用存储空间),如果文件名中包含特殊字符,它们也不受文件名的影响:

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
  mkdir -p "$destination"/`dirname "$file"`
  \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
done'

或者更快,使用“ parallel”命令同时为多CPU移动一堆文件:

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "`parallel --number-of-cores`" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
    mkdir -p "$destination"/`dirname "$file"`
    \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

PS:您有错字,“源”应该是“源”。我保留了变量名。


0

如果文件的数量/大小不太大,这不太优雅,但很容易

将您的文件一起zip压缩到一个存档中,然后在没有-j选项的情况下将其解压缩到目标位置。默认情况下,zip将创建相对目录结构。


0

尝试这种方式:

IFS=$'\n'
for f in `find "$soure" -type f -mtime "-$days"`;
do
  mkdir -p "$destination"/`dirname $f`;
  mv $f "$destination"/`dirname $f`;
done

0

因为似乎没有真正简单的解决方案,而且我经常需要它,所以我为Linux创建了这个开源实用程序(需要python):https : //github.com/benapetr/smv

有多种方法可以使用它来实现所需的功能,但最简单的方法可能是:

 # -vf = verbose + force (doesn't stop on errors)
smv -vf `find some_folder -type f -mtime "-$days"` target_folder

您还可以在干模式下运行它,以便它除了打印将执行的操作外不执行任何操作

smv -rvf `find some_folder -type f -mtime "-$days"` target_folder

或者,如果文件列表太长而无法放入参数行,并且您不介意为每个文件执行python,那么

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.