いくつかの分析を実行し、別のファイルで実行するいくつかのコマンドを準備(書き込み)するシェルスクリプトがあります。
だから私は次のようなものを持っています:
echo my_command_to_run >> /tmp/file_command_to_run.txt
プログラムがどんどん遅くなると思います。 ファイルが大きい場合(〜3M行)、プログラムに時間がかかりますか?
また、メモリに何かを保存しているので、これが問題の原因かもしれませんが、出力を別のファイルにリダイレクトする必要があるかどうかを知りたいです。 (例:2000行のファイルを複数作成)
編集:私のスクリプトは、約64M(数百万)のファイルをより良いスキーマに移動する準備をしています。それで、私はさまざまな「構造化された」フォルダをすべて見て、移動する準備をしました。
メモリには次の配列があります。
topic1 -> /path/to/my/folder
topic1_number_of_files -> nb
したがって、複数の項目(最大〜4'000)があるため、私の配列も大きくなります。
そうでない場合、ジョブの実行は常に同じです。私の配列とファイルだけが大きくなっています。
EDIT2:以下は私のスクリプトです。
メモ:
- 最大100,000個のファイルを含む複数のフォルダがあります。
- 次のものがあります。フォルダ1 - >(source1__description1、source1__description2、source2__description3)
目標:次のことを行ってください。
- ソース1/フォルダ1 ->(ソース1__説明1、ソース1__説明2)
- ソース2/フォルダ1 ->(ソース2__説明3、...)
現在のパフォーマンス:
14時間で約900,000行が挿入されました。 <=>すべての移動コマンドを準備するのに約40日かかります。
#!/bin/bash
argument=$1
if [[ -n "$argument" ]] && [[ -e $argument ]]; then
html_folder=$argument
echo "We will move [folder]/files from your parameters: '$html_folder'"
else
html_folder="/var/files/html_files/"
echo "NO PARAMETER (or folder does not exist) - We will move [folder]/files from $html_folder"
fi
######################## create the list ########################
filename="/var/files/html_files/list_folder.txt" # list generated with ls -1 -f (this doesn't take everything in memory)
ls -1 -fp $html_folder | grep '/$' | grep 'folder'> "$filename"
#################### END create the list ########################
echo " "
# --------------------------------------------------------------
# -------------- Global variables for moving part --------------
# --------------------------------------------------------------
# Variables for storing the folder/files tree
declare -A folder_array # array of folder '/files/publisher_html/10.3390' => 4 (i.e.: 4 folders for mdpi)
declare -A folder_files_array # array of files in last folder '/files/publisher_html/10.3390' => 51 (i.e.: 51 files in the 4th folders for mdpi)
storageFolder="/files/publisher_html/"
nb_limit=100000 # max number of file per folder
file_nb=0
current_folder=""
# --------------------------------------------------------------
# --------------------------------------------------------------
# --------------------------------------------------------------
# --------------------------------------------------------------
# -------------- Global functions for moving part --------------
# --------------------------------------------------------------
countNumberOfFilesPerFolder () {
nb=0
if [[ -e $1 ]]; then
nb=$(ls -1fp $1 | grep -v '/$' | wc -l )
fi
echo $nb
}
createFolderIfNeeded () {
# $1 # first arg (/path/to/htmlfiles/10.3390)
tmp_folder=""
nb_folder=1
nb_files=0
if [[ ! -e $1 ]]; then # if folder doesn't exist
sudo mkdir -p "$1/folder$nb_folder" ; # create the folders if don't exist
else
#echo "THE FOLDER $tmp_folder ALREADY EXISTED...BE AWARE!!!"
if [[ -e ${folder_array[$1]} ]]; then
nb_folder=${folder_array[$1]} # take the value from memory if available
else
nb_folder=$(ls -1f $1 | grep folder | wc -l )
fi
if (($nb_folder==0)); then # if no subfolder for the publisher folder
nb_folder=1
nb_files=0
sudo mkdir -p "$1/folder$nb_folder" # simply create the first folder
else
# if [[ -e ${folder_files_array[$1]} ]]; then
if [[ ${folder_files_array[$1]} ]]; then
nb_files=${folder_files_array[$1]} # value from memory
#echo "value from MEEEEEM: $1 => $nb_files"
else
nb_files=`countNumberOfFilesPerFolder "$1/folder$nb_folder"`
#echo "value from COOOOOOUNT: $1 => $nb_files"
fi
if (($nb_files >= $nb_limit)); then # create a new folder + reset memory value
((nb_folder++))
nb_files=0
sudo mkdir -p "$1/folder$nb_folder"
#`createFolderIfNeeded "$1/folder$nb_folder"` # NO CORRECT -> will create a subfolder
fi
fi
fi
#((nb_files++))
folder_files_array[$1]=$nb_files
folder_array[$1]=$nb_folder
current_folder="$1/folder$nb_folder" # change the global variable
}
extractPrefix() {
whotest[0]='test' || (echo 'Failure: arrays not supported in this version of
bash.' && exit 2)
array=(${1//__/ })
prefix=${array[0]}
echo $prefix
}
# --------------------------------------------------------------
# --------------------------------------------------------------
# --------------------------------------------------------------
toMoveFolder=$html_folder"toMove/"
toMoveFileIndex=1
toMoveCmdNumber=0
maxCmdInFile=2000
if [[ ! -e $toMoveFolder ]]; then # if folder doesn't exist
sudo mkdir -p $toMoveFolder ; # create the folders
fi
cd $html_folder
while read -r folder # for each folder
do
if [[ -e $folder ]]; then
echo "Will manage folder: $folder"
# ---------------------------------------------------------------------------------------------------
# -------------------------------------- MOVE INDIVIDUAL FILES --------------------------------------
# ---------------------------------------------------------------------------------------------------
argument=$html_folder$folder
cpt=0
#argument=$1
if [[ -n "$argument" ]] && [[ -e $argument ]]; then
html_files_folder=$argument
else
html_files_folder="/var/files/html_files/html_files/"
fi
######################## create the list ########################
htmlList="/var/files/html_files/list_html.txt" # list generated with ls -1 -f (this doesn't take everything in memory)
ls -1f $html_files_folder > "$htmlList" # no need to exclude the "." and ".." (we exclude from the foreach)
#################### END create the list ########################
echo " "
current_folder=$storageFolder # probably useless
while read -r line
do
name=$line
if [[ $name != "." ]] && [[ $name != ".." ]]; then # don't take the folder itself
prefix=`extractPrefix $name`
if [ -n $prefix ]; then
# change the global $current_folder
# + create new subfolder if needed
# + increment nb of files in folder
createFolderIfNeeded $storageFolder$prefix
((cpt++))
if(( $toMoveCmdNumber >= $maxCmdInFile )); then
toMoveCmdNumber=0
((toMoveFileIndex++))
fi
echo "sudo mv $html_files_folder$name $current_folder/$name" | sed -r 's/[\(\)]+/\\&/g' >> $toMoveFolder"command_"$toMoveFileIndex".txt"
((toMoveCmdNumber++))
((folder_files_array[$storageFolder$prefix]++))
if (( $cpt % 50 == 0 ));then
echo ""
echo "Remind: folder -> $current_folder/"
echo "${#folder_array[@]} publishers in memory!"
fi
echo "#$cpt - $name (${folder_files_array[$storageFolder$prefix]} files)"
else
echo "ERROR -> $name has not been moved as expected"
fi
fi
# >> $toMoveFolder"file"$toMoveFileIndex".txt" # <== does not take the toMoveFileIndex variation in consideration
done < "$htmlList" # useful if we use the while
echo "Folder $html_files_folder has been processed"
echo " "
# ---------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------------------
else # END if [[ -e $folder ]]; then
echo " "; echo "ERROR -> folder $folder does NOT exist!"; echo " "
continue
fi
done < "$filename" # useful if we use the while
echo "The script to prepare the move of the html files FROM FOLDER in other folders finished!"
echo " "
echo " "
echo "FOLDER ARRAY AT THE END: "
for i in "${!folder_array[@]}"; do echo "folder : $i => nb_folder: ${folder_array[$i]} / nb__file in last folder: ${folder_files_array[$i]}"; done
echo " "
echo " "
echo "This is the end of the script"
そしてパーティション:
$df -h
/dev/sdb1 2.0T 370G 1.7T 19% /var/files
X.X.X.X:/files 11T 2.8T 7.2T 28% /files
最後の修正:
さらなる分析の後、/var/files/html_files/が/files/html_files/へのシンボリックリンクであるため、ソースとターゲットは実際には同じ(リモート)サーバーであることがわかりました。 「リモート」サーバーでスクリプトを実行しましたが、はるかに速いようです。
あなたの助けと興味深いコメントに感謝します!
答え1
出力を別のファイルにリダイレクトする必要があるかどうか疑問に思います。 (例:2000行のファイルを複数作成)
より多くのファイルに分割しても必ずしも実行速度が速くなるわけではありません。 3つの簡単なテストケースがこの点を示しています。これら3つのケースはそれぞれ300万行を印刷します。実行速度が最も速いものから最も遅いものの順にリストされています。
ループ外からの1回のリダイレクト
for i in $(seq $((3000000/2000))); do seq 2000; done > file
ループ内で同じファイルに追加
for i in $(seq $((3000000/2000))); do seq 2000 >> file; done
出力を複数のファイルに分割
for i in $(seq $((3000000/2000))); do seq 2000 > file$i; done
以降のコマンドは、常に前のコマンドよりも多くのユーザーとシステム時間を消費します。
これから、この単純なケースでは、より多くのファイルに分割することはパフォーマンスの向上を保証しないと結論付けることができます。事実はその逆です。
I/O オペランド
パフォーマンスはファイルサイズだけでなく、IO操作の数によって異なります。追加(>>
)すると、ファイルの終わりを見つけるためにより多くのI / O呼び出しが発生します。
最初のスクリプトはi / o操作を実行します(>>
)。外部サイクルfor
:
$ cat outloop.sh
#!/bin/sh
>file
for i in $(seq 1 ${1:?})
do
echo $i
done >> file
一方、スクリプトは>>
反復ごとにi / o操作()を実行します。~へサイクルfor
:
$ cat inloop.sh
#!/bin/sh
>file
for i in $(seq 1 ${1:?})
do
echo $i >> file
done
実行して比較して、ワーカーの場所がパフォーマンス>>
にどのような影響を与えるかを確認します。
$ x=500000; time sh outloop.sh $x; time sh inloop.sh $x;
real 0m1.227s
user 0m0.389s
sys 0m0.859s
real 0m2.996s
user 0m0.809s
sys 0m2.197s
場所リダイレクト演算子外部私のシステムで500,000行を書くと、ループはパフォーマンスを2倍にします。