ubuntuusers.de

*.* p[@] sort u[@]

Status: Gelöst | Ubuntu-Version: Ubuntu 12.04 (Precise Pangolin)
Antworten |

Krümelomat

Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

Hi, ich möchte den Inhalt eines Verzeichnisses in ein Array schreiben:

p=(*.*)

Dann die Erweiterung .* abschneiden

Dann

sort -g

Dann das Ganze in ein neues Array pressen.

Ergebnis:

echo ${p[@]};
10.txt
2.txt
3.txt

echo ${u[@]};
1 (war vorher 2)
2 (war vorher 3)
3 (war vorher 10)

Eigentlich ist nur wichtig, dass die blanken Dateinamen sortiert wieder in einem Array landen.

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

1
2
3
p=( *.* )

q=( $(printf '%s\n' "${p[@]%%.*}" | sort -g) )

Bedenke aber, dass es hierbei Probleme gibt, wenn die Files bzw. Dirs Leerzeichen etc. haben.

Ich hatte hier mal einen Fall, wo ich kein sort benutzen wollte, sondern nur bash:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env bash
array=( *.* )

for (( i=1 ; i < ${#array[@]} ; ++i ))
do
    for (( j=i ; j > 0 ; --j ))
    do
        element=${array[j]}
        (( ${element%%.*} < ${array[j-1]%%.*} )) && { array[j]=${array[j-1]%%.*} ; array[j-1]=${element%%.*} ; }
    done
done

array_neu=( $(printf '%s\n' "${array[@]}") )

printf '%s\n' "${array_neu[@]}"

Das geht aber nur mit Ziffern, sonst muss mit conditional expression getestet werden (nicht arithmetic); die Operatoren müssen evtl. auch angepasst sein. Hier noch andere Ideen fürs Sortieren. Für eine vollständigere Funktion zum Sortieren siehe zB die bashlib bzw. die Funktion "order".

Krümelomat

(Themenstarter)
Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

@D630 Danke, die Syntax muss ich noch verinnerlichen.

ls *.*
10.txt  11.txt  12.txt  1.txt  2.txt  3.txt  4.txt  5.txt  6.txt  7.txt  8.txt  9.txt
p=(*.*)
u=( $(printf "%s\n" ${p[@]%.txt} | sort -g))
echo ${u[@]}
s=( $(printf "%s\n" ${p[@]} | sort -g))
echo ${s[@]}
for ((i=0; i<${#u[*]}; i++)); do echo echo ${s[i]} zu ${u[i]}; done >zz.txt

Erklärung: Jetzt habe ich die Eingangs Dateien in ein "p" Array eingelesen, sortiert, in ein "u" Array geschrieben, den Inhalt des Arrays verändert und in ein "s" Array geschrieben. Dann eine zz.txt Datei generiert die Befehle zur Transformation von "u" zu "s" enthält. Jetzt könnte ich den Inhalt von zz.txt kopieren bzw. verändern und anschließend ins Terminal kopieren und somit ausführen.

Anhang gefunden auf http://mandrivausers.org/index.php?/topic/21998-reading-a-text-file-line-by-line-with-bash/

Wie kann ich die Befehle von zz.txt direkt ins Terminal laden, so dass sie zeilenweise ausgeführt werden, so als hätte ich sie ins Terminal kopiert. Alle Versuche von mir schlugen fehl, weil die Bash sowie das Array, Leerzeichen und Zeilenvorschub als Trennungszeichen erkennen. Es wird nicht zeilenweise sondern wortweise geladen. Folgender Code führt die Zeilen der zz.txt aus:

while read line; do $line; done < zz.txt

Wie kann ich zz.txt zeilenweise in ein neues Array laden? So:

i=0; while read line; do t[i]="$line"; ((i++)); done < zz.txt
echo ${t[@]}

p.s. für Verbesserungen bin ich wie immer Ohr. Denn diese horchel ich gerne durch meine Morchel'n.

Das hier http://mywiki.wooledge.org/BashPitfalls ist sehr informativ, was die syntaktischen Fehler angeht.

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

Krümelomat schrieb:

Wie kann ich zz.txt zeilenweise in ein neues Array laden? [...]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#ZB
while read -r line
do
    array+=( "$line" )
done < zz.txt

#oder
while read -r
do
    array+=( "$REPLY" )
done < zz.txt

#oder
mapfile -t array < zz.txt

Krümelomat

(Themenstarter)
Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

mapfile -t array < zz.txt

gefällt am besten.

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

Ein bestehendes Array mit den Namen "array" würde dabei allerdings überschrieben. Notiz: mapfile ist ein Synonym für readarray.

Krümelomat

(Themenstarter)
Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

Ja, aber ich denke das ist weniger fehleranfällig als wenn neuer Inhalt in ein bestehendes Array geschrieben wird. Außerdem ist es kürzer.

Ziemlich elegant ist es auch über die IFS Variable:

IFS=$'\n'
t=($(cat zz.txt)) # t

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

Lieber mapfile oder Schleife, weil cat nicht zur bash gehört und Du $IFS zwischenspeichern müsstest, denk ich. Wenn Manipulation, dann find ich es einfacher mit read:

1
IFS=$'\n' read -r -d'' -a t < <(cat zz.txt)

edit:

Aber ich seh gerade, dann braucht man IFS gar nicht mehr verändern. Es reicht

1
read -r -d'' -a t < <(cat zz.txt)

Krümelomat

(Themenstarter)
Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

Hi, du meintest wohl:

read -r -d'\n' -a t < <(cat zz.txt)

weil das funktioniert, deins hat Wortweise gelesen, also die Leerzeichen als Trennelement zwischen den Strings erkannt und in die Felder des Arrays geladen.

D630

Avatar von D630

Anmeldungsdatum:
24. Juli 2013

Beiträge: 329

So?!

1
IFS=$'\n' read -r -d'\n' -a t < <(cat zz.txt)

Krümelomat

(Themenstarter)
Avatar von Krümelomat

Anmeldungsdatum:
18. Oktober 2010

Beiträge: 1142

Ja, so auch, so ist es quasi doppelt gegeben, weil einmal der Befehl read bei Zeilenumbruch die Zeilen trennt durch -d und wenn -d nicht da wäre, dann würde IFS dafür sorgen, dass bei Zeilenumbruch getrennt wird. Also die Zeilen hier machen was sie sollen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
i=0; while read -r line; do t[i++]="$line"; done < zz.txt

while read -r line do; t+=( "$line" ); done < zz.txt

while read -r; do t+=( "$REPLY" ); done < zz.txt

for ((i=0; i<${#u[*]}; i++)); do echo echo ${s[i]} zu ${u[i]}; done >zz.txt

IFS=$'\n'
t=($(cat zz.txt)) # t

read -r -d'\n' -a t < <(cat zz.txt)

mapfile -t t < zz.txt

echo ${t[@]}

echo ${t[2]}
echo 3.txt zu 3

Bearbeitet von rklm:

Nutz doch bitte die passende Syntaxhervorhebung.

Antworten |