Effiziente Möglichkeit, mehrere Dateien zu erstellen

7

Ich habe das Suchverzeichnis getestet, das maximal Inodes benötigt, und während des Testens war ich ausgeführt worden

touch test_{1..1391803}.txt

Aber es gibt mir einen Fehler "-bash: /usr/bin/touch: Argument list too long", jetzt laufe ich unter dem Befehl, aber es scheint, dass es Hugh Zeit brauchen wird

ruby -e '1.upto(1391803) { |n| %x( touch "test_#{n}.txt" ) }'

Die Frage ist also: Gibt es eine Möglichkeit, mehrere Dateien in kurzer Zeit zu erstellen? sollte ich 1 lac-Dateien pro Schleife berühren oder auf eine bessere Weise?

Testergebnis :

Nr. 1

[root@dc1 inode_test]# time seq 343409 | xargs touch

real    0m7.760s
user    0m0.525s
sys     0m4.385s

Nr. 2

[root@test-server inode_test]# time echo 'for (i=1;i<=343409;i++) i' | bc | xargs touch

real    0m8.781s
user    0m0.722s
sys     0m4.997s

Nr. 3

[root@test-server inode_test]# time printf '%s ' {1..343409} | xargs touch

real    0m8.913s
user    0m1.144s
sys     0m4.541s

Nummer 4

[root@test-server inode_test]# time awk 'BEGIN {for (i=1; i<=343409; i++) {printf "" >> i; close(i)}}'

real    0m12.185s
user    0m2.005s
sys     0m6.057s

Nr. 5

[root@test-server inode_test]# time ruby -e '1.upto(343409) { |n| File.open("#{n}", "w") {} }'

real    0m12.650s
user    0m3.017s
sys     0m4.878s
Rahul Patil
quelle

Antworten:

12

Die Einschränkung liegt in der Größe der Argumente bei Ausführung eines Befehls. Sie können also einen Befehl mit weniger Argumenten ausführen, z. B. xargskleinere Stapel ulimit -s 100000ausführen , das Limit erhöhen ( unter Linux), nichts ausführen (alles in der Shell ausführen) oder die Liste in dem Tool erstellen, das das erstellt Dateien.

zsh, ksh93, bash:

printf '%s ' {1..1391803} | xargs touch

printfist eingebaut, also gibt es keine exec, also ist das Limit nicht erreicht. xargsteilt die Liste der übergebenen Argumente auf touch, um zu vermeiden, dass das Limit überschritten wird. Das ist immer noch nicht sehr effizient, da die Shell zuerst die gesamte Liste erstellen muss (besonders langsam mit bash), sie im Speicher speichern und dann drucken muss.

seq 1391803 | xargs touch

(vorausgesetzt, Sie haben einen seqBefehl) wäre effizienter.

for ((i=1; i<=1391803; i++)); do : >> "$i"; done

Alles wird in der Shell erledigt, keine große Liste im Speicher gespeichert. Sollte relativ effizient sein, außer vielleicht mit bash.

POSIXly:

i=1; while [ "$i" -le 1391803 ]; do : >> "$i"; i=$(($i + 1)); done

echo 'for (i=1;i<=1391803;i++) i' | bc | xargs touch

awk 'BEGIN {for (i=1; i<=1391803; i++) {printf "" >> i; close(i)}}'
Stéphane Chazelas
quelle
3

Sie sind durch die maximale Anzahl von Argumenten begrenzt, touchdie verarbeitet werden können. Die beste Wette wäre, eine Schleife zu verwenden. Dafür brauchst du allerdings keinen Rubin:

for i in $(seq 1391803); do touch test_${i}.txt; done

Ein alternativer Ansatz könnte darin bestehen, die Zahl in Blöcke aufzuteilen, z. B. 100, und diese dann jeweils zu füttern touch:

i=1; while ((i<=1391803)); do touch $(seq $i $((i+99))); i=$((i+100)); done
devnull
quelle
Das Limit gilt nicht für, touchsondern für den execve()Systemaufruf (für die kumulative Größe der Argumente und Umgebungsvariablen, die an diesen Aufruf übergeben wurden).
Stéphane Chazelas
+1, da dies bei weitem die schnellste ist, sogar noch schneller, wenn Sie die Befehle der Pipe parallel ausführen. Die akzeptierte Lösung hätte über 1 Stunde gebraucht, um meine 10 Millionen Dateien zu erstellen, dies in weniger als 1 Minute
user1084563
3

In Ihrem Beispiel beschwert sich Bash, weil beim Erweitern test_{1..1391803}.txteine zu lange Argumentbefehlszeile angezeigt wird. Die maximale Länge der Befehlszeile, die an einen Befehl übergeben werden kann, wird vom Kernel festgelegt, da der exec Systemaufruf, der für das Starten neuer Prozesse (das Ersetzen des Programms eines vorhandenen Prozesses durch einen anderen) verantwortlich ist, diese Argumente eingeben muss Der Stapel des Prozesses und die Größe des Stapels sind begrenzt.

Ich denke, der effizienteste Weg, dies zu tun, wäre, nicht touchjedes Mal einen neuen Prozess zu starten, wenn Sie eine Datei möchten.

Sie könnten in Rubin zum Beispiel:

ruby -e '1.upto(1391803) { |n| File.open("test_#{n}.txt", "w") {} }'

Auf diese Weise starten Sie nur einen Prozess, der alle Dateien erstellt, ohne dass das touchProgramm gestartet werden muss.

Dieser Befehl startet den Ruby-Interpreter. Dann erstellt Ruby eine Schleife über den Bereich 1..1391803und ruft für jede Nummer die Funktion auf File.open, die den openSystemaufruf mit einem Dateinamen ausführt, der mit der Nummer erstellt wurde. Da der Block danach File.openleer ist, wird die Datei sofort geschlossen.

lgeorget
quelle
Könnten Sie bitte erklären, wie das funktioniert?
Rahul Patil
Ich habe meine Antwort aktualisiert. Zögern Sie nicht zu fragen, ob noch Zweifel bestehen. :)
Lgeorget