簡體   English   中英

如何從文件列表中刪除路徑部分並將其復制到另一個文件中?

[英]How to remove the path part from a list of files and copy it into another file?

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

  • 創建一個目錄。
  • 生成1000個唯一文件,其名稱取自系統中的其他隨機文件。
  • 每個文件必須包含有關其名稱所在的原始文件的信息 - 名稱和大小,而不包含文件的原始內容。
  • 該腳本必須以毫秒為單位顯示有關其執行速度的信息。

我能做的是用命令findgrep 1000個唯一文件的名稱和路徑,並將它們放在一個列表中。 然后我無法想象如何刪除路徑部分並在其他目錄中創建文件,其名稱取自隨機文件列表。 我嘗試了一個帶有basename命令的for循環,但不知怎的,我無法讓它工作,我也不知道如何做其他任務......

[ 更新 :我想回到這個問題,試圖讓我的響應在平台上更有用和可移植 (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標志的一個-fprintf是它似乎在處理文件名中的空格。 通過使用vimsed檢查文件( 查找帶空格的行)並比較wc -luniq的輸出,您可以了解輸出以及生成的列表是否合理。 然后你可以通過cutgrepsedawk和friends管道,以便以你想要的方式創建文件。 例如,從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找到,是不是POSIX標准標志,這樣它將無法使用在OS X或BSD find(1)雖然GNU發現可能對你的Unix安裝作為gfindgnufind )。 一種更便攜的方法是使用find / -type f > tmp.txt創建一個直接的文件列表(這在我的系統上需要大約15秒,在ZFS池中有800k文件和許多慢速驅動器。一些更有效的東西應該很容易讓人們在評論中做!)。 從那里,您可以使用標准實用程序創建所需的數據值,以處理文件列表,如上面的Florin Stingaciu所示。

#!/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.dat文件,這不是我們想要的! 那么,如何從我們的800k列表中隨機選擇1000個文件進行處理? 有幾種方法可以解決它。

從文件列表中隨機選擇

我們列出了系統上的所有文件(!)。 現在,為了選擇1000個文件,我們只需要從列表文件( tmp.txt )中隨機選擇1000行。 我們可以通過使用你在上面看到的酷od技術生成一個隨機數來設置行號的上限 - 它是如此酷和跨平台,我把這個別名放在我的shell中;-) - 然后執行模數除法% )使用文件中的行數作為除數。 然后我們只取這個數字,然后選擇與awk或sed對應的文件中的行( 例如 sed -n <$RANDOMNUMBER>p filelist ),迭代1000次並預先設置! 我們有一個包含1000個隨機文件的新列表。 或者不......它真的很慢! 在尋找加速awksed我遇到了一個很好的技巧,使用來自Alex Lines的dd按字節(而不是行)搜索文件,並使用sedawk將結果轉換為一行。 有關詳細信息,請參閱Alex的博客 我的技術唯一的問題是將count= switch設置為足夠高的數字。 出於神秘的原因(我希望有人會解釋) - 也許是因為我的localeLC_ALL=en_US.UTF-8 - dd會將不完整的行吐出到randlist.txt除非我將count=設置為更高的數字,即實際的最大行長度。 我想我可能會混淆字符和字節。 有什么解釋嗎?

因此,在上述警告之后,並希望它可以在兩個以上的平台上運行,這是我嘗試解決問題的方法:

#!/bin/sh
IFS='
'                                                                                
# We create tmp.txt with                                                        
# find / -type f > tmp.txt  # tweak as needed.                                  
#                                                                               
files="tmp.txt"                                                           

# Get the number of lines and maximum line length for later                                                                              
bytesize=`wc -c < $files`                                                 
# wc -L is not POSIX and we need to multiply so:
linelenx10=`awk '{if(length > x) {x=length; y = $0} }END{print x*10}' $files`

# A function to generate a random number modulo the                             
# number of bytes in the file. We'll use this to find a                         
# random location in our file where we can grab a line                          
# using dd and sed. 

genrand () {                                                                    
  echo `od -An -N 4 -D < /dev/urandom` ' % ' $bytesize | bc                     
}                                                                               

rm -f randlist.txt                                                             

i=1                                                                             
while [ $i -le 1000 ]                                                          
do                             
 # This probably works but is way too slow: sed -n `genrand`p $files                
 # Instead, use Alex Lines' dd seek method:
 dd if=$files skip=`genrand` ibs=1 count=$linelenx10 2>/dev/null |awk 'NR==2 {print;exit}'>> randlist.txt

 true $((i=i+1))    # Bourne shell equivalent of $i++ iteration    
done  

for file in `cat randlist.txt`                                                 
  do                                                                           
   name=`basename $file`                                                        
   size=`wc -c <"$file"`                                 
   echo -e "Location: $file \n\n Size: $size" > $name.dat  
  done    

# vim: ft=sh 

我能做的是用命令“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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM