我有大量文件(数万个)需要从 /dir1 复制到 /dir2,但文件的名称必须按如下方式更改:
旧名称--->新名字
文件名.txt ---> bob_文件名_1253.txt
其中 bob 是拥有该文件的 uid,1253 是上次修改的时间。
我目前正在使用一个 python 脚本实现这一点,该脚本循环遍历每个文件,然后将其 cp 到新目的地,并通过字符串切片创建新的文件名。
然而,这需要花费大量的时间。有没有更干净、更快捷的方法来实现这一点?
提前致谢!
答案1
假设你从一个空文件夹开始,这样你就可以在不发生冲突的情况dir2
下将文件从复制dir1
到dir2
然后重命名它们,我会尝试这样的事情:
printf '%s\0' dir1/*.txt | xargs -r0 cp -np -t dir2/
执行复制,然后
cd dir2/
printf '%s\0' *.txt | xargs -r0 rename -n -- '
$_ = join "_", ( getpwuid( (stat $_)[4] ) )[0], $_, (stat $_)[9]
'
重命名(对建议的更改满意后,-n
从命令中删除)。rename
虽然它最常用于简单的s/pattern/replacement/
名称更改,但 Ubuntu 当前版本中提供的基于 Perl 的rename
命令实际上可以根据几乎任意的 Perl 表达式重命名文件。
在这种情况下,我们可以使用 Perl 的内置功能stat
来获取数字 UID 和 mtime,然后使用getpwuid
将 UID 转换为用户名。(stat $_)[9]
是以纪元秒为单位的修改时间 (mtime) - 如果需要其他格式,您可以使用POSIX::strftime
或其他几个时间操作模块之一。
答案2
将*.txt
文件从目录复制dir1/
到dir2/
目录并重命名:
$ ls -l 共 8 drwxr-xr-x 2 用户 用户 4096 лют 19 01:06 dir1 drwxr-xr-x 2 用户 用户 4096 лют 19 01:08 dir2 $ ls dir1 名称1.txt 名称2.txt 名称3.txt
$ 表示 ./dir1/* 中的文件路径; 执行用户名=$(stat -c '%U' $filepath) && 文件日期=$(stat -c '%Y' $文件路径) && newpartname='dir2/'$n'_'“${filepath//'./dir1/'}”&& cp $filepath "${newpartname//'.txt'}"_$filedate.txt; 完毕
ls 目录2/ 用户名称1_1582067151.txt 用户名称2_1582067158.txt 用户名称3_1582067165.txt
您说重命名需要花费很多时间。但是您的所有文件的大小是多少?您知道复制所有文件需要的时间吗?
答案3
以下脚本应该可以完成工作。尝试一下,当你对结果满意时,删除#
前面的注释cp -p "$oldpath""$f9" "$newpath""$new"
以执行实际工作。
该脚本将在您的主目录中创建一个名为的文件ls_file.txt
。此文件用于存储ls -l "$oldpath"
命令的输出,脚本在执行期间需要从中读取。脚本执行完成后,该ls_file.txt
文件不再需要,您可以将其删除。
始终备份
要创建和使用脚本文件,请按照以下步骤操作:
通过在终端中运行以下命令,在主目录中创建并编辑脚本文件:
nano ~/my_script.sh
将以下代码复制并粘贴到编辑器中,并将其更改
/full/path/to/old/directory/
为目录的完整路径/dir1
和目录/full/path/to/new/directory/
的完整路径/dir2
:
#!/bin/bash
oldpath="/full/path/to/old/directory/"
newpath="/full/path/to/new/directory/"
ls -l "$oldpath" > "ls_file.txt"
while read f1 f2 f3 f4 f5 f6 f7 f8 f9
do
mod="${f6}${f7}${f8}"
mod2=$(echo "$mod" | tr -d ' :')
name="${f9%.*}"
ext="${f9##*.}"
new="$f3""_""$name""_""$mod2"".""$ext"
echo "$f9"" ---> Will be renamed ---> ""$new"
echo "----------------------------"
# cp -p "$oldpath""$f9" "$newpath""$new"
done < "ls_file.txt"
保存脚本文件并按Ctrl+X然后按退出编辑器Y。
通过在终端中运行以下命令使脚本文件可执行:
chmod +x ~/my_script.sh
通过在终端中运行以下命令来运行脚本:
bash ~/my_script.sh
完毕
怎么运行的?
dir1/
从我的主目录在包含一个文件file.txt
和一个dir2/
为空的测试目录上运行脚本,同时指定oldpath="/home/raffa/test/dir1/"
和newpath="/home/raffa/test/dir2/"
,以下过程是脚本如何工作的示例:
- 该命令
ls -l /home/raffa/test/dir1/
被执行并且输出被写入一个名为的文件ls_file.txt
,该文件将包含文件的属性和名称(在dir1/
本例中为一个文件),file.txt
如下所示:
-rw-r--r-- 1 raffa raffa 0 Jan 25 19:49 file.txt
-rw-r--r--
是文件模式。1
是链接的数量。- 第一个
raffa
是主人的名字。 - 第二个
raffa
是团体的名称。 0
是文件大小。Jan 25 19:49
(三个字段)是最后修改的日期和时间。file.txt
是文件的名称。
. 2. 脚本会逐行读取 的内容dir1/
并将ls_file.txt
每行拆分为字段(共九个),并将每个字段的值赋给一个变量f1 f2 f3 f4 f5 f6 f7 f8 f9
。
mod="${f6}${f7}${f8}"
将把存储在变量中的最后修改日期和时间分配f6 f7 f8
给变量mod
,因此它将包含如下值:
Jan2519:49
mod2=$(echo "$mod" | tr -d ' :')
将获取变量的值$mod
,删除任何空格和冒号:
并将其分配给变量mod2
,因此它将包含如下值:
Jan251949
name="${f9%.*}"
将会把不带扩展名的文件名分配给变量name
,因此它将包含如下值:
file
ext="${f9##*.}"
将会把不带名称的文件扩展名分配给变量ext
,因此它将包含如下值:
txt
new="$f3""_""$name""_""$mod2"".""$ext"
将把新文件名格式化为owner_name_lastmodified.extension
并将其分配给变量new
,因此它将包含如下值:
raffa_file_Jan251949.txt
cp -p "$oldpath""$f9" "$newpath""$new"
将会把原始文件file.txt
从复制dir1/
到dir2/
并将其重命名为raffa_file_Jan251949.txt
,并尝试在此过程中保留文件的原始属性,因此它将像这样执行:
cp -p /home/raffa/test/dir1/file.txt /home/raffa/test/dir2/raffa_file_Jan251949.txt
该过程将针对具有任何扩展名的所有文件重复进行dir1/
。
另一种时间格式:
如果您希望最后修改日期和时间格式如下,20200125133815
其中2020
是年份 ,01
是月份 ,25
是日期 ,13
是 24 格式的小时 ,38
是分钟 ,15
是秒 ,那么您复制的文件名将如下所示raffa_file_20200125133815.txt
,请使用以下代码替换上面的脚本代码:
#!/bin/bash
oldpath="/full/path/to/old/directory/"
newpath="/full/path/to/new/directory/"
ls -l --time-style="+%Y%m%d%H%M%S" "$oldpath" > "ls_file.txt"
while read f1 f2 f3 f4 f5 f6 f7
do
mod="${f6}"
name="${f7%.*}"
ext="${f7##*.}"
new="$f3""_""$name""_""$mod"".""$ext"
echo "$f7"" ---> Will be renamed ---> ""$new"
echo "----------------------------"
# cp -p "$oldpath""$f7" "$newpath""$new"
done < "ls_file.txt"
注意:
在某些情况下解析输出ls
也许可以更好地避免。