Mojo_Dodo
Anmeldungsdatum: 31. Januar 2008
Beiträge: Zähle...
|
Hallo, ich habe ein Vielzahl von Dateien deren Dateinamen (mit Pfad) ich gerne in einem Array sortiert hätte.
Dabei ist das System nach dem diese Dateien benannt sind wohl das größte Problem. Leider habe ich auf die Dateinamen keinen Einfluss. Die Dateien beginnen immer mit einer Zahl die entweder 2 oder 3 Ziffern lang ist. Danach kommt genau ein Buchstabe (i.d.R. G, K oder L), daraufhin eine Zahl die entweder nur 1 oder 2 Ziffern lang ist und im Anschluss daran noch eine 1 oder eine 2. Also hier mal zu Veranschaulichung: 20K21 20K121 20K122 20K62 20K11 Sortiert haben möchte ich alle meine Daten nach der Zahl nach dem Buchstabe (diese kann ja entweder aus einer oder zwei Ziffern bestehen). Bei zwei Dateien die sich nur hinten durch eine 1 oder 2 entscheiden, soll dann die mit der 1 vornestehen.
Die korrekte Reihenfolge wäre demnach: 20K11 20K21 20K62 20K121 20K122 Das rein alphabetische sortieren habe ich mit einem Skript, das ich gefunden habe hinbekommen:
| array1=~/test/*.CSV
array2=( $( for i in "${array2[@]}"
do
echo "$i"
done | sort) )
|
Ich denke, dass das ganze etwas einfacher wäre wenn beispielsweise die Datei 20K062 anstatt 20K62 hieße. Leider habe ich wie gesagt keinen Einfluss darauf wie die Dateien benannt werden. Übrigens: das ganze muss keinesfalls zwangsläufig mit sort geschehen. Jedes andere Kommandozeilentool ist auch in Ordnung. Ich hatte sort nur genommen weil es mir am naheliegensten schien. Über jegliche Vorschläge würde ich mich sehr freuen!
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Hey, wie wäre es mit soetwas $ ls -1 *.CSV
20K11.CSV
20K121.CSV
20K122.CSV
20K21.CSV
20K62.CSV
$ ls *.CSV | sed -r 's/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2
20K11.CSV
20K21.CSV
20K62.CSV
20K121.CSV
20K122.CSV Grüße Frink
|
Mojo_Dodo
(Themenstarter)
Anmeldungsdatum: 31. Januar 2008
Beiträge: 212
|
Hey Prof. Frink, vielen Dank für Deine Antwort. Leider kenne ich mich mit sed noch sehr schlecht aus. Ich werde mich aber gleich mal dranmachen den Befehl nachzuvollziehen. Bei einem kurzen Probedurchlauf hat der Code bei mir gerade aber leider nicht funktioniert:
| ls ~/test/*.CSV | sed -r 's/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2
20K11.CSV
20K121.CSV
20K122.CSV
20K21.CSV
20K62.CSV
|
Hast du den Befehl bei dir so 1 zu 1 ausprobiert? Gruß
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Mojo Dodo schrieb: Hast du den Befehl bei dir so 1 zu 1 ausprobiert?
Sorry, das habe ich nicht abgefangen, funktioniert so nur im lokalen Verzeichnis, wenn man ihn von einem anderen Verzeichnis ausführt, muss man ihn noch leicht modifizieren: $ ls /tmp/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2
/tmp/test/20K11.CSV
/tmp/test/20K21.CSV
/tmp/test/20K62.CSV
/tmp/test/20K121.CSV
/tmp/test/20K122.CSV
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13174
|
Prof. Frink schrieb: Mojo Dodo schrieb: Hast du den Befehl bei dir so 1 zu 1 ausprobiert?
Sorry, das habe ich nicht abgefangen, funktioniert so nur im lokalen Verzeichnis, wenn man ihn von einem anderen Verzeichnis ausführt, muss man ihn noch leicht modifizieren:
Es fehlt auch die Sortierung nach der letzten Ziffer. Hier mal eine Lösung in Ruby: $ ls -1
20K121
20K122
20K21
20K62
21K11
$ ruby -e 'a=[];Dir["#{ARGV.first}/*"].each {|f| %r{\A\d{2,3}[a-z](\d{1,2})([12])\z}i =~ File.basename(f) and a << [$1.to_i, $2.to_i, f]};puts a.sort_by {|i,j,f| [i,j]}.map(&:last)' .
./21K11
./20K21
./20K62
./20K121
./20K122 Nochmal der Ruby-Teil: | a=[]
Dir["#{ARGV.first}/*"].each {|f| %r{\A\d{2,3}[a-z](\d{1,2})([12])\z}i =~ File.basename(f) and a << [$1.to_i, $2.to_i, f]}
puts a.sort_by {|i,j,f| [i,j]}.map(&:last)
|
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
rklm schrieb: Es fehlt auch die Sortierung nach der letzten Ziffer.
Wieso? Funktioniert doch: $ echo "
/tmp/test/20K122.CSV
/tmp/test/20K121.CSV" | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2
/tmp/test/20K121.CSV
/tmp/test/20K122.CSV
|
Mojo_Dodo
(Themenstarter)
Anmeldungsdatum: 31. Januar 2008
Beiträge: 212
|
Hey Prof. Frink, danke, das funktioniert! Du scheinst Dich mit sed ja ziemlich gut auszukennen. Beim Versuch zu verstehen was der Code macht ist mir leider noch nicht alles klar geworden.
Vom Prinzip her funktioniert es ja so, dass die Zahl nach dem Buchstaben vor den Dateinamen geschrieben wird, dann kann sort sortieren und dann schneidet cut die Zahlen vorne wieder weg, richtig?
Ich habe gelesen, dass \1 für das steht, was in der Klammer gefunden wird (also dieser Teil: ([0-9]{1,2}) ) .Steht \0 für den gesamten gefundenen Teil aus dem "Suchen-Abschnitt" des sed-Befehls? Besten Dank nochmals für die Hilfe! @ rklm
Das mit der letzten Ziffer ist mir nicht aufgefallen, da es in meinem Beispiel schon automatisch gestimmt hat. Danke für den Code! Mit Ruby kann ich leider gar nichts anfangen; ich werde eventuell bei Zeit mal einen Blick darauf werfen, bin aber froh wenn ich meine Sachen mit den "Bash-Bordmitteln erledigen kann - auch damit sich der Aufwand für mich in Grenzen hält und ich am Schluss nicht doch noch schneller dran wäre wenn ich 200 Dateien manuell sortieren würde 😉
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Mojo Dodo schrieb: Vom Prinzip her funktioniert es ja so, dass die Zahl nach dem Buchstaben vor den Dateinamen geschrieben wird, dann kann sort sortieren und dann schneidet cut die Zahlen vorne wieder weg, richtig?
Ja. Ich habe gelesen, dass \1 für das steht, was in der Klammer gefunden wird (also dieser Teil: ([0-9]{1,2}) ) .
Ja. Steht \0 für den gesamten gefundenen Teil aus dem "Suchen-Abschnitt" des sed-Befehls?
Ja. ☺ Grüße Frink
|
Mojo_Dodo
(Themenstarter)
Anmeldungsdatum: 31. Januar 2008
Beiträge: 212
|
noch eine letzte Frage hätte ich dazu: wie bekomme ich die einzelnen Dateien nun in einen Array?
| array1=`ls ~/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2`
|
speichert alle Dateien unter Position 0 in meinem Array ab.
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Einfach klammern, wie du es in deinem ersten Post eigentlich auch schon hattest: | array1=(`ls ~/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2`)
|
Grüße Frink
|
Mojo_Dodo
(Themenstarter)
Anmeldungsdatum: 31. Januar 2008
Beiträge: 212
|
Prof. Frink schrieb: Einfach klammern, wie du es in deinem ersten Post eigentlich auch schon hattest: | array1=(`ls ~/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2`)
|
Stimmt. Ich hatte beim klammern bei mir gerade die Apostrophen vergessen.
Es funktioniert entweder wie von Dir gerade vorgeschlagen oder auch mit:
| array1=( $(ls ~/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2) )
|
👍
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13174
|
Prof. Frink schrieb: rklm schrieb: Es fehlt auch die Sortierung nach der letzten Ziffer.
Wieso? Funktioniert doch:
Nein: $ echo "/tmp/test/20K121.CSV
/tmp/test/120K122.CSV" | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{1,2})[12]{1}\.CSV/\1 \0/g' | sort -n | cut -d " " -f 2
/tmp/test/120K122.CSV
/tmp/test/20K121.CSV Grund ist, dass Du das letzte Feld (die Ziffer, die entweder 1 oder 2 ist) nicht extrahierst und nach vorne bringst. Übrigens ist die Angabe "{1}" total überflüssig. Das /g brauchst Du auch nicht, denn es soll ja genau ein Mal ersetzt werden, und man erwartet nicht, dass sich das Muster in der Zeile wiederholt. Genau genommen sollte man das Muster auch am Zeilenanfang und -ende ankern. Sonst werden nämlich auch Dateinamen verarbeitet, die nicht dem vorgegebenen Muster entsprechen. Um das zu verhindern kann man die sed -Option -n benutzen und an den s/// die Option /p anhängen. Wenn überhaupt mit sed , dann eher so $ echo "/tmp/test/20K121.CSV
0123ignore-this
/tmp/test/20K11.CSV
/tmp/test/120K122.CSV" | sed -nr 's#^.*/[0-9]{2,3}[A-Z]([0-9]{1,2})([12])\.CSV$#0\1\2 \0#p' | sed -r 's#^0*([0-9]{3})#\1#' | sort -n | sed -r 's#^[0-9]+ ##'
/tmp/test/20K11.CSV
/tmp/test/20K121.CSV
/tmp/test/120K122.CSV Oder man macht sich Felder bei sort zunutze: $ echo "/tmp/test/20K121.CSV
0123ignore-this
/tmp/test/20K11.CSV
/tmp/test/120K122.CSV" | sed -nr 's#^.*/[0-9]{2,3}[A-Z]([0-9]{1,2})([12])\.CSV$#\1:\2:\0#p' | sort -n -t : -k 1,2 | sed -r 's#^([0-9]+:){2}##'
/tmp/test/20K11.CSV
/tmp/test/20K121.CSV
/tmp/test/120K122.CSV Das ist eigentlich das Sauberste.
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Übrigens macht die künstliche Trennung der letzten Ziffer von ihren Vorläufern in diesem Fall keinen Sinn, da das Ergebnis dasselbe ist, wenn man alles was nach dem Buchstaben kommt als eine Zahl auffasst: $ ls /tmp/test/*.CSV | sed -r 's/.*\/[0-9]{2,3}[A-Z]([0-9]{2,3})\.CSV/\1\t\0/g' | sort -n -k1 | cut -f 2
/tmp/test/20K11.CSV
/tmp/test/20K21.CSV
/tmp/test/20K62.CSV
/tmp/test/20K121.CSV
/tmp/test/20K122.CSV
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
@rklm
Okay, du hast recht. Aber wie ich gerade schon bemerkt habe, braucht man die letzte Ziffer nicht künstlich zu trenen, also sollte es unter Berücksichtigung deiner Anmerkungen auch so funktionieren echo "/tmp/test/20K121.CSV
0123ignore-this
/tmp/test/20K11.CSV
/tmp/test/120K122.CSV" | sed -nr 's/.*\/[0-9]{2,3}[A-Z]([0-9]{2,3})\.CSV/\1\t\0/p' | sort -n -k1 | cut -f 2
/tmp/test/20K11.CSV
/tmp/test/20K121.CSV
/tmp/test/120K122.CSV
Danke für die Korrektur. Grüße Frink
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17604
Wohnort: Berlin
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | echo -e "20K21
20K121
20K122\n21K31
20K62\n20L72\n20L71
20K11
" | sed -r 's/^([0-9]+)([a-zA-Z]+)(.*)([12])$/\1 \2 \3 \4/' | sort -V
20 K 1 1
20 K 2 1
20 K 6 2
20 K 12 1
20 K 12 2
20 L 7 1
20 L 7 2
21 K 3 1
|
Ich habe natürlich auch einen Vorschlag. Die Felder isolieren mit sed - die 4 Gruppen sind: (Zeilenanfang, keine Gruppe) Zahlen bis der Arzt kommt ein Buchstabe etwas bis der Arzt kommt eine Eins oder zwei (Zeilenende, keine Gruppe)
sort -v sortiert als sei es eine Versionsnummer. Das scheint mir passend. Mit tr -deleted man dann die als Hilfe inserierten Blanks: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | echo -e "20K21
20K121
20K122\n21K31
20K62\n20L72\n20L71
20K11
" | sed -r 's/^([0-9]+)([a-zA-Z])(.*)([12])$/\1 \2 \3 \4/' | sort -V | tr -d " "
20K11
20K21
20K62
20K121
20K122
20L71
20L72
21K31
|
|