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?
Antwort
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}
Warum nicht? weil es komplett kaputt ist! Unterbrochene Dateinamen enthalten Leerzeichen, Zeilenumbrüche oder globale Zeichen. –
@gniourf_gniourf Sie hatten absolut Recht. Ist das besser? – bluerojo
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) '. –
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[@]}
Bitte, [ls 'nicht analysieren] (http://mywiki.wooledge.org/ParsingLs). –
@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). –
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). –
#!/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, dieawk
läuft, so Namen zuawk
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 vonawk
. Mit bash 4.4 oder neuer können Sie erkennen, wenn eine Prozesssubstitution fehlschlägt, indem Siewait "$!" || { 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.
Stellen Sie sicher, dass Ihre Frage genügend Informationen enthält, um das Problem zu reproduzieren. –
Bitte schauen Sie: http://www.shellcheck.net/ – Cyrus
... 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. –