Frage

Mit find . -print0 der einzig sichere Weg zu erhalten, eine Liste von Dateien in bash aufgrund der Möglichkeit von Dateinamen mit Leerzeichen zu sein scheint, Zeilenumbrüche, Anführungszeichen, etc.

Aber ich habe eine harte Zeit, die tatsächlich die Ausgabe innerhalb bash nützlich machen finden oder mit anderen Befehlszeilen-Dienstprogramme. Die einzige Art, wie ich es geschafft haben, die Verwendung der Ausgabe zu machen, ist durch kochend es Perl und Perl IFS auf null zu ändern:

find . -print0 | perl -e '$/="\0"; @files=<>; print $#files;'

Dieses Beispiel gibt die Anzahl der Dateien gefunden, die Gefahr von Zeilenumbrüchen in Dateinamen korrumpieren die Zählung zu vermeiden, wie bei auftreten würde:

find . | wc -l

Da die meist Kommandozeilen-Programme nicht auf Null begrenzten Eingang unterstützen, ich meine, das Beste, was die Ausgabe von find . -print0 in einem Bash-Array zu erfassen wäre, wie ich in der Perl-Code-Schnipsel oben getan habe, und dann mit der Aufgabe weiter , was auch immer es auch sein mag.

Wie kann ich das tun?

Das funktioniert nicht:

find . -print0 | ( IFS=$'\0' ; array=( $( cat ) ) ; echo ${#array[@]} )

Eine viel allgemeinere Frage könnte sein: Wie kann ich nützliche Dinge mit Listen von Dateien in bash tun

?
War es hilfreich?

Lösung

Schamlos gestohlen von Gregs BashFAQ :

unset a i
while IFS= read -r -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done < <(find /tmp -type f -print0)

Beachten Sie, dass die Umleitung Konstrukt verwendet hier (cmd1 < <(cmd2)) ist ähnlich, aber nicht ganz das gleiche wie die üblicheren Pipeline (cmd2 | cmd1) - wenn die Befehle Shell builtins (zB while) sind, die Pipeline-Version führt sie in Subshells und alle Variablen sie (zB dem Array a) sind verloren, wenn sie Ausfahrt. cmd1 < <(cmd2) läuft nur cmd2 in einer Subshell, so das Array lebt über seine Konstruktion. Achtung: diese Form der Umleitung ist nur in bash verfügbar, auch nicht bash in sh-Emulationsmodus; Sie müssen Ihr Skript mit #!/bin/bash starten.

Auch, weil der Dateiverarbeitungsschritt (in diesem Fall nur a[i++]="$file", aber Sie könnten etwas ausgefallenere direkt in der Schleife tun wollen) seine Eingabe umgeleitet, es kann keine Befehle verwenden, das von der Standardeingabe lesen könnte. Um diese Einschränkung zu vermeiden, neige ich dazu zu verwenden:

unset a i
while IFS= read -r -u3 -d $'\0' file; do
    a[i++]="$file"        # or however you want to process each file
done 3< <(find /tmp -type f -print0)

..., die die Dateiliste über die Einheit passiert 3, statt stdin.

Andere Tipps

Vielleicht suchen Sie xargs:

find . -print0 | xargs -r0 do_something_useful

Die Option -L 1 könnte auch für Sie nützlich sein, die xargs exec do_something_useful mit nur 1 Dateiargument macht.

Das Hauptproblem ist, dass das Trennzeichen NUL (\ 0) nutzlos ist hier, weil es nicht möglich ist, IFS einen NUL-Wert zuweisen. So wie gute Programmierer kümmern wir uns, dass der Eingang für unser Programm ist etwas, es in der Lage ist, zu behandeln.

Zuerst erstellen wir ein kleines Programm, das diesen Teil für uns tut:

#!/bin/bash
printf "%s" "$@" | base64

... und nennt es base64str (nicht vergessen chmod + x)

Zweitens können wir nun eine einfache und unkomplizierte for-Schleife verwenden:

for i in `find -type f -exec base64str '{}' \;`
do 
  file="`echo -n "$i" | base64 -d`"
  # do something with file
done

Also der Trick ist, dass ein Base64-String kein Zeichen hat, die Probleme für bash verursacht -. Natürlich eine xxd oder etwas ähnliches auch die Arbeit machen kann

Eine weitere Möglichkeit, Dateien zu zählen:

find /DIR -type f -print0 | tr -dc '\0' | wc -c 

Da Bash 4.4, der eingebaute mapfile den -d Schalter hat (zu spezifizieren ein Trennzeichen, ähnlich den -d Schalter der read-Anweisung), und der Begrenzer kann der Null-Byte sein. Daher ist eine nette Antwort auf die Frage im Titel

  

Capturing Ausgang find . -print0 in eine bash array

ist:

mapfile -d '' ary < <(find . -print0)

Sie können die Zählung mit dieser sicher tun:

find . -exec echo ';' | wc -l

(Es druckt eine neue Zeile für jede Datei / Verzeichnis gefunden, und dann die Zeilenumbrüche zählen ausgedruckt ...)

Ich denke, elegantere Lösungen existiert, aber ich werde diese in werfen Dies wird auch für Dateinamen mit Leerzeichen arbeiten und / oder Zeilenumbrüche:.

i=0;
for f in *; do
  array[$i]="$f"
  ((i++))
done

Sie können dann z Liste der Dateien nacheinander (in diesem Fall in umgekehrter Reihenfolge):

for ((i = $i - 1; i >= 0; i--)); do
  ls -al "${array[$i]}"
done

Diese Seite gibt ein schönes Beispiel, und für mehr finden Sie unter Kapitel 26 in der Erweiterte Bash-Scripting Guide .

Vermeiden Sie xargs, wenn Sie können:

man ruby | less -p 777 
IFS=$'\777' 
#array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' \; 2>/dev/null) ) 
array=( $(find ~ -maxdepth 1 -type f -exec printf "%s\777" '{}' + 2>/dev/null) ) 
echo ${#array[@]} 
printf "%s\n" "${array[@]}" | nl 
echo "${array[0]}" 
IFS=$' \t\n' 

Ich bin neu, aber ich glaube, dass dies eine Antwort; hoffe, es hilft jemand:

STYLE="$HOME/.fluxbox/styles/"

declare -a array1

LISTING=`find $HOME/.fluxbox/styles/ -print0 -maxdepth 1 -type f`


echo $LISTING
array1=( `echo $LISTING`)
TAR_SOURCE=`echo ${array1[@]}`

#tar czvf ~/FluxieStyles.tgz $TAR_SOURCE

Dies ist ähnlich Stephan202-Version, aber die Dateien (und Verzeichnisse) werden in ein Array auf einmal setzen. Die for Schleife ist hier nur zu „tun nützliche Dinge“:

files=(*)                        # put files in current directory into an array
i=0
for file in "${files[@]}"
do
    echo "File ${i}: ${file}"    # do something useful 
    let i++
done

eine Zählung erhalten:

echo ${#files[@]}

Alte Frage, aber niemand vorgeschlagen, diese einfache Methode, also dachte ich, ich würde. Zugegeben, wenn Ihre Dateinamen einen ETX haben, ist das Ihr Problem nicht lösen, aber ich vermute, dass es für jedes reales Szenario dient. Der Versuch, null zu verwenden scheint in Konflikt geraten Standard-IFS Umgang mit Regeln ausgeführt werden. Saison nach Geschmack mit Fund Optionen und Fehlerbehandlung.

savedFS="$IFS"
IFS=$'\x3'
filenames=(`find wherever -printf %p$'\x3'`)
IFS="$savedFS"

Gordon Davisson Antwort ist für bash. Jedoch nützliche Abkürzungen existieren für zsh Benutzer:

Zuerst legen Sie String in einer Variablen:

A="$(find /tmp -type f -print0)"

Als nächstes wird aufgespalten diese Variable und speichern sie in einem Array:

B=( ${(s/^@/)A} )

Es gibt einen Trick: ^@ das NUL-Zeichen ist. Um das zu machen, müssen Sie Strg + V durch Ctrl gefolgt geben + @.

Sie können jeden Eintrag von $ B überprüfen enthält richtigen Wert:

for i in "$B[@]"; echo \"$i\"

Eine sorgfältige Leser können diesen Anruf bemerken Befehl find kann in den meisten Fällen mit ** Syntax vermieden werden. Zum Beispiel:

B=( /tmp/** )

Bash hat im Umgang mit Dateinamen nie gut (oder irgendein Text wirklich), weil es nutzt Räume als Liste Trennzeichen.

würde ich empfehlen, Python mit der sh Bibliothek statt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top