3

FreeBSD の bash スクリプトで次のことを達成する必要があります。

  • ディレクトリを作成します。
  • システム内の他のランダム ファイルから名前が取得された 1000 個の一意のファイルを生成します。
  • 各ファイルには、名前が付けられた元のファイルに関する情報 (ファイルの元の内容を除いた名前とサイズ) が含まれている必要があります。
  • スクリプトは、その実行速度に関する情報をミリ秒単位で表示する必要があります。

私が達成できたのは、コマンドfindを使用して 1000 個の一意のファイルの名前とパスを取得し、grepそれらをリストに入れることでした。次に、パス部分を削除して、ランダムなファイルのリストから取得した名前で他のディレクトリにファイルを作成する方法を想像できません。コマンドを使用して for ループを試しましたbasenameが、どういうわけかそれを機能させることができず、他のタスクの実行方法もわかりません...

4

2 に答える 2

3

[更新:元の質問では bash と zsh が指定されていましたが、この質問に戻って、プラットフォーム (OS X は Unix です!) と $SHELL 間で私の応答をより便利で移植性の高いものにしようと思いました。他の回答は、リストがどのように作成されたか、または選択がどのように行われたかを質問が示していなかったため、「ランダムな」ファイル名の一時ファイル リストを想定していました。応答で、一時ファイルを使用してリストを作成する 1 つの方法を示します。どうすればランダム化できるかわかりませんfind操作は「インライン」であり、他の誰かがこれがどのように(移植可能に)行われるかを示してくれることを願っています。また、これがいくつかのコメントや批評を引き付けることを願っています。私は perl のリファレンスを削除しましたが、これを perl で再度実行し、perl は移植性が高いため、Windows 上で実行できるようにすることに挑戦します。コメントを待ってから、この回答を短くしてクリーンアップします。ありがとう。]

ファイル リストの作成

GNU find(1) で多くのことができます。以下は、ファイル名と、必要なデータの 3 つのタブ区切り列 (ファイルの名前、場所、キロバイト単位のサイズ) を含む単一のファイルを作成します。

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

ファイル システム全体からエントリを取得するために、すべてのファイル名をランダムにする(つまり、リンクを作成しない) ことを想定しています。私のワークステーションには 800000 個のファイルがありますが、大量の RAM があるため、これを行うのにそれほど時間はかかりません。私のラップトップには約 300K のファイルがあり、メモリはあまりありませんが、完全なリストを作成するのに数分しかかかりませんでした。検索から特定のディレクトリを除外または削除して調整する必要があります。

フラグの良いところは-fprintf、ファイル名のスペースを処理しているように見えることです。vimandを使用してファイルを調べsed(つまり、スペースを含む行を探す)、 and の出力を比較するwc -lことuniqで、出力の感覚をつかみ、結果のリストが正常かどうかを確認できます。cut次に、これを、grepまたはsed、および友人にパイプして、必要awkな方法でファイルを作成できます。たとえば、シェルプロンプトから:

~/# 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 では使用できないことです(ただし、GNU find は Unix にまたはfind(1)としてインストールされる場合があります)。これを行うためのより移植性の高い方法は、ファイルのまっすぐなリストを作成することです(これには、ZFS プールに 800k ファイルと多くの低速ドライブがある私のシステムでは約 15 秒かかります。より効率的なものを考え出すことは、人々にとって簡単なはずです。コメントでやってください!)。そこから、標準ユーティリティを使用して必要なデータ値を作成し、Florin Stingaciu が上に示したようにファイル リストを処理できます。gfindgnufindfind / -type f > tmp.txt

#!/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 個のファイルを選択するには、リスト ファイル ( ) からランダムに 1000 行を選択する必要がありますtmp.txt。上で見たクールな手法を使用して乱数を生成することにより、選択する行番号の上限を設定できますod。非常にクールでクロスプラットフォームであるため、シェルでこれにエイリアスを設定しています ;-) - 次に、モジュロ除算を実行します( %)ファイル内の行数を除数として使用します。次に、その番号を取得して、それが awk または sed に対応するファイル内の行を選択し (例: sed -n <$RANDOMNUMBER>p filelist )、1000 回反復してさっさと! 1000 個のランダム ファイルの新しいリストがあります。かどうか...本当に遅いです!スピードアップする方法を探しているawksed(行ではなく) バイト単位でファイルを検索し、結果をorddを使用して行に変換する Alex Lines を使用した優れたトリックです。詳しくはアレックスのブログをご覧ください。彼のテクニックに関する私の唯一の問題は、スイッチを十分に高い数値に設定することでした。不思議な理由で (誰かが説明してくれることを願っています)、おそらく私のせいで、実際の最大行長よりもはるかに大きな数に設定しない限り、不完全な行が 吐き出され ます。おそらく文字とバイトを混同していたと思います。説明はありますか?sedawkcount=localeLC_ALL=en_US.UTF-8ddrandlist.txtcount=

したがって、上記の警告の後、2 つ以上のプラットフォームで動作することを期待して、問題を解決するための私の試みを次に示します。

#!/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 
于 2013-05-25T16:48:44.210 に答える
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
于 2013-05-23T19:34:48.383 に答える