2016-06-16 9 views
0

Ich versuche, ein Array von Dateinamen zu erstellen, so dass ich eine große Datei sortieren und nur diejenigen nehmen kann, die ich möchte. Mein Skript sieht so aus set STATIONS = Kanto-station-names Wo der Ordner "Kanto-Station-Namen" die Namen der Dateien, die ich möchte. Dies wird von myarr=($(awk '{print $1}') $STATIONS) gefolgt, aber Terminal geht sofort in eine Subshell, die eher wie ein Arbeitsdokument aussieht, wo Befehle nicht funktionieren und ich kann nicht beenden. Irgendwelche Vorschläge?Wie kann ich mein Bash-Array reparieren?

+1

Stellen Sie sicher, dass Ihre Frage genügend Informationen enthält, um das Problem zu reproduzieren. –

+3

Bitte schauen Sie: http://www.shellcheck.net/ – Cyrus

+0

... auch, im allgemeinen, um * alles * in ein Shell-Array zu lesen, sind die entsprechenden Werkzeuge 'readarray -t' /' mapfile -t' oder mindestens "read -a" oder eine "while read" -Schleife, die eine Append-Operation durchführt. 'arr = ($ (...))' ist sehr, * sehr * fehleranfällig und wird am besten vermieden. –

Antwort

0

Warum erstellen Sie nicht einfach ein Array mit dem Befehl find, um Ihre Liste von Dateinamen zu erhalten?

Ex:

#!/bin/bash 
IFS=$'\n' 
MyArr=($(find /location/to/Kanto-station-names/ -type f -print0 | xargs -0 ls)) 

echo ${MyArr[@]} 
echo ${MyArr[1} 
echo ${MyArr[2} 
echo ${MyArr[3} 
+3

Warum nicht? weil es komplett kaputt ist! Unterbrochene Dateinamen enthalten Leerzeichen, Zeilenumbrüche oder globale Zeichen. –

+0

@gniourf_gniourf Sie hatten absolut Recht. Ist das besser? – bluerojo

+1

Nun ... es ist immer noch kaputt! Sie unterliegen immer noch der Pfadnamenerweiterung (dies kann mit 'set -f' behoben werden) und ist immer noch mit Dateinamen mit Zeilenumbrüchen unterbrochen (und dies kann nicht mit dieser Methode behoben werden); Es ist eigentlich immer eine schlechte Idee, solche Befehle zu analysieren; Wenn Sie _really_ jedoch (GNU) 'find' auf 100% sichere Weise verwenden möchten, tun Sie dies:' MyArr =(); während IFS = read -r -d '' Datei; do MyArr + = ("$ Datei"); done <<(find/location/to/Kanto-Stationsname/-type f -print0) '. –

0

Ihre Frage ist schwer zu verstehen, aber wenn Sie nur ein Array aller Dateinamen in Kanto-Station-Namen-Verzeichnis vornehmen wollen, sollte diese Arbeit (beachten Sie, dass der führende ' $‘ist eine Aufforderung, nicht etwas, das man geben sollte):

$ myArray=() 
$ for FILE in `ls Kanto-station-names` ; do myArray+=($FILE) ; done 

Danach können Sie Array-Elemente zugreifen:

$ echo ${myArray[0]} 
$ echo ${myArray[1]} 

usw. Um alle Elemente des Arrays zu sehen:

$ echo ${myArray[@]} 
+3

Bitte, [ls 'nicht analysieren] (http://mywiki.wooledge.org/ParsingLs). –

+0

@gniourf Es stimmt, wenn einer Ihrer Dateinamen Leerzeichen enthält, funktioniert das Obige nicht. Das Beispiel, mit dem Sie verlinkt haben, hat sogar Dateinamen mit Zeilenumbrüchen, was schrecklich ist. Das heißt, ich parse "ls" die ganze Zeit ohne Probleme. Ich habe einfach eine Richtlinie, Dateien nicht mit Whitespace zu benennen. IMO, es ist böse, Dateinamen zu haben, die mehr als ein Token sind (normalerweise in Win und Mac zu sehen, nicht so sehr in * nix). –

+1

Was böse ist, ist irgendetwas über Dateinamen anzunehmen. Ein Dateiname ist eine C-Zeichenfolge, die nicht '/' enthält. Zeitraum. Wenn Sie feststellen, dass Sie 'ls' analysieren, dann tun Sie offensichtlich etwas falsches: Sie verstehen nicht den _word splitting und globbing_ Teil davon, wie Shells Befehle parsen und daher nicht die richtige Semantik verwenden! Außerdem spawnen Sie eine Subshell und verwenden einen externen Befehl, und das ist wirklich nicht effizient. Verwenden Sie stattdessen Globs: Es ist 100% sicher, kürzer zu tippen und effizienter (und es ist semantisch korrekt). –

0
#!/usr/bin/env bash 

# cause globs to expand to an empty list if no matches exist 
shopt -s nullglob 

# Use a glob expression to populate the array 
stations=(Kanto-station-names/*) 

((${#stations[@]})) || { 
    echo "ERROR: No stations exist (make sure Kanto-station-names contains files)" >&2 
    exit 1 
} 

## read into an array (bash 4.0 or newer) 
#readarray -t myarr < <(awk '{print $1}' "${stations[@]}") 

# read into an array (bash 3.x-compatible) 
IFS=$'\n' read -r -d '' -a myarr < <(awk '{print $1}' "${stations[@]}" && printf '\0') || { 
    echo "Error extracting first column from station list" >&2 
    exit 1 
} 

# print definition of our populated array as output 
declare -p myarr 

Artikel der Anmerkung:

  • Wir ls nicht in diesem Code verwenden. Die gesamte Erstellung von Listen von Dateinamen wird mit Globbing durchgeführt. Siehe Why you shouldn't parse the output of ls(1).
  • "${stations[@]}" ist innerhalb der Prozess Substitution erweitert, die awk läuft, so Namen zu awk geben werden - das war einer der Fehler in dem ursprünglichen Code.
  • Der Code readarray in der auskommentierten Alternative für Bash 4.0 erkennt keinen fehlgeschlagenen Beendigungsstatus von awk. Mit bash 4.4 oder neuer können Sie erkennen, wenn eine Prozesssubstitution fehlschlägt, indem Sie wait "$!" || { echo "Process failed" >&2; } o.ä. ausführen - vor diesem Zeitpunkt kann der Exit-Status einer Prozesssubstitution nicht erkannt werden. Es gibt also zwingende Gründe, die bash 3.x- zu verwenden. kompatibler Ansatz sogar mit bash 4.0 bis 4.3.