我的 Ubuntu 10.10 台式机上有一个大型数据目录(20-30Gb),其中包含许多原始数据文件、处理后的数据文件以及从处理后的数据生成的各种脚本、表格、图形等。数据目录已经积累了很多年,而且结构很差——“有一天”我会把它整理出来,但总有更重要的事情要做。
我现在正在切换到在线备份服务,为了减少备份所需的时间和所需的在线存储,我想拆分原始数据,这占用了大量空间,但很容易替换为已在其他地方存档,同时保留其在目录结构中的一般位置。换句话说,我想从这样的地方开始:
/data/A/raw1.data
/data/A/raw2.data
/data/A/raw3.data
/data/A/processed.txt
/data/A/figure.eps
/data/A/plot.gnu
/data/B/raw4.data
/data/B/processed.txt
... etc.
到
/data/A/processed.txt
/data/A/figure.eps
/data/A/plot.gnu
/data/B/processed.txt
... etc.
和
/raw_data/A/raw1.data
/raw_data/A/raw2.data
/raw_data/A/raw3.data
/raw_data/B/raw4.data
... etc.
因此,原始数据文件从 /data 交换到 /raw_data,但保留其在目录结构中的位置,而处理后的数据和关联文件保留在同一位置。整体文件结构比这复杂和无序得多,但可取之处是所有原始数据都可以通过文件类型(主要是.fits和.sdf)来识别。
我确信,通过正确的命令组合和/或几行 bash 脚本,这是微不足道的,但我的命令行知识仅限于基础知识,我宁愿问也不愿冒险搞砸:)
而且,顺便说一句,有没有一种简单的方法来查找原始数据中的重复项 - 将具有相同的文件名+大小,不一定是时间戳,当从存档下载数据时,时间戳会被重置,尽管要完全确定我需要通过 dfits 管道每个重复的候选者并 grep 适合标头中的时间戳。
答案1
一种方法是使用rsync
一些特制的包含/排除规则以及在同步后删除源文件的选项,如下所示:
rsync -av --include "*/" --include='*.fits' --include='*.sdf' \
--exclude='*' --remove-source-files /data/ /raw_data/
如果您想在循环中逐步移动,以便可能包含其他操作,则需要一个执行如下操作的脚本:
DIR1="/data"
DIR2="/raw_data"
find "$DIR1" -type f \( -iname '*.fits' -or -iname '*.sdf' \) -print0 |
while read -d $'\0' file; do
mkdir -p "$DIR2/$(basename "$file")"
mv "$file" "$DIR2/$(basename "$file")"
done
答案2
有很多文件复制工具允许使用足够灵活的规则构建目标目录名称(zcp
、rsync
、pax
、...)。不幸的是,它们中很少有允许按需移动(而不是复制)和创建目标目录。因此,我将展示一些分两遍完成的方法:首先创建所有可能必需的目标目录,然后执行移动。
Perl重命名
rename
如果您编写了必要的 Perl 部分,Debian 和 Ubuntu 附带的Perl程序可以在需要时创建目标目录。
shopt -s globstar # make **/ traverse directories recursively (requires bash 4)
rename 'BEGIN {use File::Path}
s!^/data!/raw_data!;
m!(.*)/!; mkpath($1)' /data/**/*.raw
在 zsh 中,省略该shopt -s globstar
行;**
默认表示递归遍历。在 bash 和 zsh 之外的 shell 中,您需要使用find
递归遍历(请参见下面的示例)。如果您只有单级目录,则不必担心这一切。
创建目标目录
在 zsh 中(解释:/
全局限定符表示仅匹配目录,并且e
glob 限定符将后面给出的转换应用于每个名称):
mkdir /data/**/*(/e\''REPLY=${REPLY/data/raw_data}'\')
与其他外壳:
find /data -type d \
-exec sh -c 'for d; do mkdir "/raw_data${d#/data}"; done' _ {} +
如果你只有一层子目录,那就简单多了:
for d in /data/*/; do mkdir "/raw_data${d#/data}"; done
移动文件 (zsh)
autoload zmv
zmv -Q '/data/(**/)(*.raw)(.)' '/raw_data/$1$2'
移动文件(便携式)
find /data -name '*.raw' \
-exec sh -c 'for x; do mv "$x" "/raw_data${x#/data}"; done' _ {} +