GNU/Linux >> Linux Esercitazione >  >> Linux

Itera su un elenco di file con spazi

C'è anche una soluzione molto semplice:affidarsi a 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'

Nota che non sono sicuro che questo comportamento sia quello predefinito, ma non vedo alcuna impostazione speciale nel mio negozio, quindi direi che dovrebbe essere "sicuro" (testato su osx e ubuntu).


Esistono diversi modi praticabili per ottenere questo risultato.

Se vuoi restare fedele alla tua versione originale, puoi farlo in questo modo:

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

Ciò fallirà comunque se i nomi dei file contengono letterali newline, ma gli spazi non lo interromperanno.

Tuttavia, non è necessario scherzare con IFS. Ecco il mio modo preferito per farlo:

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

Se trovi il < <(command) sintassi sconosciuta dovresti leggere sulla sostituzione dei processi. Il vantaggio di questo rispetto a for file in $(find ...) è che i file con spazi, newline e altri caratteri vengono gestiti correttamente. Funziona perché find con -print0 userà un null (ovvero \0 ) come terminatore per ogni nome di file e, a differenza della nuova riga, null non è un carattere legale in un nome di file.

Il vantaggio di questo rispetto alla versione quasi equivalente

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

È che qualsiasi assegnazione variabile nel corpo del ciclo while viene preservata. Cioè, se esegui il pipe a while come sopra quindi il corpo del while è in una subshell che potrebbe non essere ciò che desideri.

Il vantaggio della versione di sostituzione del processo rispetto a find ... -print0 | xargs -0 è minimo:il xargs version va bene se tutto ciò di cui hai bisogno è stampare una riga o eseguire una singola operazione sul file, ma se devi eseguire più passaggi la versione loop è più semplice.

MODIFICA :Ecco un simpatico script di test in modo che tu possa avere un'idea della differenza tra i diversi tentativi di risolvere questo problema

#!/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"

Potresti sostituire l'iterazione basata sulla parola con una basata sulla riga:

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

Linux
  1. Come trovare tutti i file con dimensione del file zero (0) byte in una directory in modo ricorsivo

  2. Come posso generare un elenco di file con il loro percorso assoluto in Linux?

  3. Trovare file leggibili dall'uomo su unix

  4. Escludi elenco di file da find

  5. elenca/trova tutti i file regolari in tutte le sottodirectory esclusi i file binari

Come trasferire file con Rsync su SSH

Comando Trova Linux con esempi pratici

Come trovare file in Debian 10

Trova i file persi con Scalpel

Come trovare file basati su timestamp in Linux

Come trovare file con dozzine di criteri con il comando Trova Bash