Vzhledem k těmto názvům souborů:
$ ls -1
file
file name
otherfile
bash
sám o sobě funguje naprosto dobře s vloženými mezerami:
$ 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
#?
Někdy se mi však může stát, že nebudu chtít pracovat s každým souborem, nebo dokonce striktně v $PWD
, což je místo find
přichází. Což také nominálně zpracovává mezery:
$ find -type f -name file*
./file
./file name
./directory/file
./directory/file name
Snažím se vymyslet whispace bezpečnou verzi tohoto skriptletu, která převezme výstup find
a předložte jej do select
:
$ select file in $(find -type f -name file); do echo $file; break; done
1) ./file
2) ./directory/file
To však exploduje s mezerami v názvech souborů:
$ 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
Normálně bych to obešel tak, že bych si pohrál s IFS
. Nicméně:
$ 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'
Jaké je toto řešení?
Přijatá odpověď:
Pokud potřebujete zpracovávat pouze mezery a tabulátory (ne vložené nové řádky), můžete použít mapfile
(nebo jeho synonymum, readarray
) pro čtení do pole, např. daný
$ ls -1
file
other file
somefile
pak
$ 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
Pokud uděláte potřebujete zpracovat nové řádky a váš bash
verze poskytuje mapfile
oddělený nulou , pak to můžete upravit na IFS= mapfile -t -d '' files < <(find . -type f -print0)
. V opačném případě sestavte ekvivalentní pole z find
odděleného nulou výstup pomocí read
smyčka:
$ 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
-d
možnost byla přidána do mapfile
v bash
verze 4.4 irc