Dati questi nomi di file:
$ ls -1
file
file name
otherfile
bash
di per sé funziona perfettamente con gli spazi bianchi incorporati:
$ for file in *; do echo "$file"; done
file
file name
otherfile
$ select file in *; do echo "$file"; done
1) file
2) file name
3) otherfile
#?
Tuttavia, a volte potrei non voler lavorare con tutti i file, o anche rigorosamente in $PWD
, che è dove find
entra. Che gestisce anche lo spazio bianco nominalmente:
$ find -type f -name file*
./file
./file name
./directory/file
./directory/file name
Sto cercando di inventare una versione whispace-safe di questo scriptlet che prenderà l'output di find
e presentalo in select
:
$ select file in $(find -type f -name file); do echo $file; break; done
1) ./file
2) ./directory/file
Tuttavia, questo esplode con uno spazio bianco nei nomi dei file:
$ select file in $(find -type f -name file*); do echo $file; break; done
1) ./file 3) name 5) ./directory/file
2) ./file 4) ./directory/file 6) name
Di solito, aggiravo questo problema scherzando con IFS
. Tuttavia:
$ IFS=$'n' select file in $(find -type f -name file*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
$ IFS='n' select file in $(find -type f -name file*); do echo $file; break; done
-bash: syntax error near unexpected token `do'
Qual è la soluzione a questo?
Risposta accettata:
Se hai solo bisogno di gestire spazi e tabulazioni (non le nuove righe incorporate), puoi usare mapfile
(o il suo sinonimo, readarray
) per leggere in un array, ad es. dato
$ ls -1
file
other file
somefile
poi
$ IFS= mapfile -t files < <(find . -type f)
$ select f in "${files[@]}"; do ls "$f"; break; done
1) ./file
2) ./somefile
3) ./other file
#? 3
./other file
Se fai devi gestire le nuove righe e il tuo bash
version fornisce un mapfile
delimitato da null , quindi puoi modificarlo in IFS= mapfile -t -d '' files < <(find . -type f -print0)
. Altrimenti, assembla un array equivalente da find
delimitato da null output usando una read
ciclo:
$ touch $'filenamenwithnnewlines'
$
$ files=()
$ while IFS= read -r -d '' f; do files+=("$f"); done < <(find . -type f -print0)
$
$ select f in "${files[@]}"; do ls "$f"; break; done
1) ./file
2) ./somefile
3) ./other file
4) ./filename
with
newlines
#? 4
./filename?with?newlines
il -d
l'opzione è stata aggiunta a mapfile
in bash
versione 4.4 iirc