GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Iterieren Sie über eine Liste von Dateien mit Leerzeichen

Es gibt auch eine sehr einfache Lösung:Verlassen Sie sich auf Bash Globbing

$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid   file 3"
$ ls
stupid   file 3  stupid file1     stupid file2
$ for file in *; do echo "file: '${file}'"; done
file: 'stupid   file 3'
file: 'stupid file1'
file: 'stupid file2'

Beachten Sie, dass ich nicht sicher bin, ob dieses Verhalten das Standardverhalten ist, aber ich sehe keine spezielle Einstellung in meinem Shopt, also würde ich sagen, dass es "sicher" sein sollte (getestet auf OSX und Ubuntu).


Es gibt mehrere praktikable Möglichkeiten, dies zu erreichen.

Wenn Sie eng an Ihrer Originalversion festhalten möchten, können Sie dies folgendermaßen tun:

getlist() {
        IFS=$'\n'
        for file in $(find . -iname 'foo*') ; do
                printf 'File found: %s\n' "$file"
        done
}

Dies wird immer noch fehlschlagen, wenn Dateinamen wörtliche Zeilenumbrüche enthalten, aber Leerzeichen werden es nicht unterbrechen.

Es ist jedoch nicht erforderlich, mit IFS herumzuspielen. Hier ist meine bevorzugte Vorgehensweise:

getlist() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: %s\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

Wenn Sie den < <(command) finden Syntax ungewohnt sollten Sie sich über die Prozesssubstitution informieren. Der Vorteil gegenüber for file in $(find ...) ist, dass Dateien mit Leerzeichen, Zeilenumbrüchen und anderen Zeichen korrekt behandelt werden. Das funktioniert, weil find mit -print0 verwendet einen null (auch bekannt als \0 ) als Abschlusszeichen für jeden Dateinamen und im Gegensatz zu Newline ist null kein gültiges Zeichen in einem Dateinamen.

Der Vorteil gegenüber der nahezu gleichwertigen Version

getlist() {
        find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
                printf 'File found: %s\n' "$file"
        done
}

Ist das jede Variablenzuweisung im Körper der While-Schleife erhalten? Das heißt, wenn Sie zu while leiten wie oben dann der Körper der while befindet sich in einer Subshell, die möglicherweise nicht das ist, was Sie wollen.

Der Vorteil der Prozesssubstitutionsversion gegenüber find ... -print0 | xargs -0 ist minimal:Die xargs Version ist in Ordnung, wenn Sie nur eine Zeile drucken oder eine einzelne Operation an der Datei ausführen müssen, aber wenn Sie mehrere Schritte ausführen müssen, ist die Schleifenversion einfacher.

BEARBEITEN :Hier ist ein nettes Testskript, damit Sie sich ein Bild von den Unterschieden zwischen verschiedenen Versuchen zur Lösung dieses Problems machen können

#!/usr/bin/env bash

dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"

touch       'file not starting foo' foo foobar barfoo 'foo with spaces'\
    'foo with'$'\n'newline 'foo with trailing whitespace      '

# while with process substitution, null terminated, empty IFS
getlist0() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# while with process substitution, null terminated, default IFS
getlist1() {
    while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# pipe to while, newline terminated
getlist2() {
    find . -iname 'foo*' | while read -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# pipe to while, null terminated
getlist3() {
    find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, default IFS
getlist4() {
    for file in "$(find . -iname 'foo*')" ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, newline IFS
getlist5() {
    IFS=$'\n'
    for file in $(find . -iname 'foo*') ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}


# see how they run
for n in {0..5} ; do
    printf '\n\ngetlist%d:\n' $n
    eval getlist$n
done

rm -rf "$dir"

Sie könnten die wortbasierte Iteration durch eine zeilenbasierte ersetzen:

find . -iname "foo*" | while read f
do
    # ... loop body
done

Linux
  1. So finden Sie rekursiv alle Dateien mit einer Dateigröße von Null (0) Bytes in einem Verzeichnis

  2. Wie kann ich unter Linux eine Liste von Dateien mit ihrem absoluten Pfad erstellen?

  3. Dateien finden und taren (mit Leerzeichen)

  4. Alle Grafikdateien mit find auflisten?

  5. listet/findet alle regulären Dateien in allen Unterverzeichnissen außer Binärdateien

So übertragen Sie Dateien mit Rsync über SSH

Linux-Suchbefehl mit praktischen Beispielen

So finden Sie Dateien in Debian 10

Finden Sie verlorene Dateien mit Scalpel

So finden Sie Dateien basierend auf dem Zeitstempel in Linux

So finden Sie Dateien mit Dutzenden von Kriterien mit dem Bash-Suchbefehl