如何从文件列表中删除路径部分并将其复制到另一个文件中?

时间:2013-05-23 19:21:13

标签: bash shell filenames freebsd

我需要在FreeBSD中使用bash脚本完成以下操作:

  • 创建目录。
  • 生成1000个唯一文件,其名称取自系统中的其他随机文件。
  • 每个文件必须包含有关其名称所在的原始文件的信息 - 名称和大小,不包含文件的原始内容。
  • 脚本必须以毫秒显示有关其执行速度的信息。

我能做的就是使用命令findgrep获取1000个唯一文件的名称和路径,并将它们放在一个列表中。然后我无法想象如何删除路径部分并在其他目录中创建文件,其名称取自随机文件列表。我尝试了一个带有basename命令的for循环,但不知怎的,我无法让它工作,我也不知道如何做其他任务......

2 个答案:

答案 0 :(得分:3)

[更新:我想回到这个问题,尝试让我的响应更有用,并且跨平台可移植(OS X是Unix!)和$ SHELL,即使原始问题指定了bash和zsh。其他回复假定临时文件列表中的“随机”文件名,因为该问题未显示列表的构建方式或选择方式。我展示了一种使用临时文件在我的响应中构建列表的方法。我不确定如何将find操作“内联”随机化,并希望其他人可以展示如何(可移植)完成此操作。我也希望这会引起一些评论和批评:你永远不会知道太多$ SHELL技巧。我删除了perl引用,但我特此挑战自己在perl中再次执行此操作 - 因为perl非常便携 - 让它在Windows上运行。我会等待一段时间的评论,然后缩短并清理这个答案。感谢。]

创建文件列表

你可以用GNU find(1)做很多事情。以下内容将创建一个文件名和一个选项卡分隔的所需数据列的单个文件(文件名,位置,大小,以千字节为单位)。

find / -type f -fprintf tmp.txt '%f\t%h/%f\t%k \n'

我假设你想要在所有文件名没有链接)中随机,所以你将从整个文件系统中获取条目。我的工作站上有800000个文件,但RAM很多,所以这不需要太长时间。我的笔记本电脑有大约300K文件,没有太多内存,但创建完整的列表仍然只需要几分钟左右。您需要通过从搜索中排除或修剪某些目录来进行调整。

-fprintf标志的一个好处是它似乎在处理文件名中的空格。通过使用vimsed检查文件(查找带空格的行)并比较wc -luniq的输出,您可以获得你的输出感以及最终的列表是否合理。然后,您可以通过cutgrepsedawk和朋友进行管道传输,以便按照您想要的方式创建文件。例如,从shell提示符:

~/# touch `cat tmp.txt |cut -f1` 
~/# for i in `cat tmp.txt|cut -f1`; do cat tmp.txt | grep $i > $i.dat ; done

我在这里给我们创建.dat扩展名的文件,以区别于他们所引用的文件,并且更容易移动它们或删除它们,你不必这样做那:只是不用分机$i > $i

关于-fprintf标志的错误事情是,它仅适用于GNU find并且不是POSIX标准标志,因此它不会在OS X或BSD上可用{ {1}}(虽然GNU find可以在你的Unix上安装为find(1)gfind)。一种更便携的方法是使用gnufind创建一个直接的文件列表(在我的系统上需要大约15秒,其中包含800k文件和ZFS池中的许多慢速驱动器。提供更有效的方法应该人们在评论中很容易做到!)。从那里,您可以使用标准实用程序创建所需的数据值,以处理上面显示的Florin Stingaciu文件列表。

find / -type f > tmp.txt

如果您一直关注这一点,您会发现这会产生大量文件 - 在我的工作站上这将创建800k #!/bin/sh # portably get a random number (OS X, BSD, Linux and $SHELLs w/o $RANDOM) randnum=`od -An -N 4 -D < /dev/urandom` ; echo $randnum for file in `cat tmp.txt` do name=`basename $file` size=`wc -c $file |awk '{print $1}'` # Uncomment the next line to see the values on STDOUT # printf "Location: $name \nSize: $size \n" # Uncomment the next line to put data into the respective .dat files # printf "Location: $file \nSize: $size \n" > $name.dat done # vim: ft=sh 文件,这不是我们的想!那么,如何从我们的800k列表中随机选择1000个文件进行处理?有几种方法可以解决它。

从文件列表中随机选择

我们列出了系统上的所有文件(!)。现在,为了选择1000个文件,我们只需要从列表文件(.dat)中随机选择1000行。我们可以通过使用您在上面看到的酷tmp.txt技术生成一个随机数来设置行号的上限 - 它太酷了,跨平台,我把这个别名放在我的shell中;-) - 然后使用文件中的行数作为除数对其执行modulo divisionod)。然后我们只取这个数字,然后在文件中选择与awk或sed对应的行(例如 %),迭代1000次并预先设置!我们有一个包含1000个随机文件的新列表。或者不......它真的很慢!在寻找加快sed -n <$RANDOMNUMBER>p filelistawk速度的方法时,我发现了一个优秀技巧,使用来自Alex Lines的sed来按字节搜索文件(而不是())并使用ddsed将结果转换为一行。 有关详细信息,请参阅Alex's blog。我的技术唯一的问题是将awk开关设置为足够高的数字。出于神秘的原因(我希望有人会解释) - 也许是因为我的count= locale - LC_ALL=en_US.UTF-8会将不完整的行吐出dd,除非我设置randlist.txt比实际最大线长更高的数字。我想我可能会混淆字符和字节。有什么解释吗?

因此,经过上述警告并希望它能在两个以上的平台上运行,这是我尝试解决问题的方法:

count=

答案 1 :(得分:1)

  

我能做的是用命令“find”和“grep”获取1000个唯一文件的名称和路径,并将它们放在列表中

我将假设有一个文件在每一行上保存每个文件的完整路径(FULL_PATH_TO_LIST_FILE)。考虑到这个过程没有太多的统计数据,我省略了。但是,您可以添加自己的。

cd WHEREVER_YOU_WANT_TO_CREATE_NEW_FILES
for file_path in `cat FULL_PATH_TO_LIST_FILE`
do
     ## This extracts only the file name from the path
     file_name=`basename $file_path`

     ## This grabs the files size in bytes
     file_size=`wc -c < $file_path`

     ## Create the file and place info regarding original file within new file
     echo -e "$file_name \nThis file is $file_size bytes "> $file_name

done