GNU/Linux >> Znalost Linux >  >> Linux

Bash:Bezpečné procedurální použití funkce Najít do výběru?

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

Související:acing -Najdi význam- Vyhledávání ve slovníku!
Linux
  1. Jak používat příkazy historie Bash

  2. The Bash ‘?

  3. Jak přihlásit každý příkaz zadaný do Bash a každou operaci se souborem?

  1. Najít soubor protokolu Firefoxu?

  2. Jakou metodu používá Unzip k nalezení jednoho souboru v archivu?

  3. Funkce Bash pro nalezení nejnovějšího vzoru pro shodu souborů

  1. bash najít adresáře

  2. Je možné použít / v názvu souboru?

  3. Rekurzivně měnit přípony souborů v Bash