Atualização 2020 para usuários Linux:
Se você tem uma versão atualizada do bash (4.4-alpha ou melhor), como provavelmente tem se estiver no Linux, então você deve usar a resposta de Benjamin W ..
Se você estiver no Mac OS, que - pelo menos eu verifiquei - ainda usava o bash 3.2 ou, de outra forma, está usando um bash mais antigo, continue na próxima seção.
Resposta para bash 4.3 ou anterior
Aqui está uma solução para obter a saída de find
em uma bash
matriz:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Isso é complicado porque, em geral, os nomes dos arquivos podem ter espaços, novas linhas e outros caracteres hostis ao script. A única maneira de usar find
e ter os nomes dos arquivos separados uns dos outros com segurança é usar o -print0
que imprime os nomes dos arquivos separados por um caractere nulo. Isso não seria muito inconveniente se as funções readarray
/ do bash mapfile
suportassem strings separadas por nulos, mas não o fazem. O de Bash read
sim e isso nos leva ao loop acima.
[Esta resposta foi escrita originalmente em 2014. Se você tiver uma versão recente do bash, consulte a atualização abaixo.]
Como funciona
A primeira linha cria uma matriz vazia: array=()
Cada vez que a read
instrução é executada, um nome de arquivo separado por nulo é lido da entrada padrão. A -r
opção diz read
para deixar os caracteres de barra invertida sozinhos. O -d $'\0'
informa read
que a entrada será separada por nulos. Desde que omitir o nome para read
, a concha coloca a entrada para o nome padrão: REPLY
.
A array+=("$REPLY")
instrução anexa o novo nome do arquivo ao array array
.
A linha final combina redirecionamento e substituição de comando para fornecer a saída find
para a entrada padrão do while
loop.
Por que usar a substituição de processo?
Se não usássemos a substituição de processo, o loop poderia ser escrito como:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
Acima, a saída de find
é armazenada em um arquivo temporário e esse arquivo é usado como entrada padrão para o loop while. A ideia da substituição do processo é tornar esses arquivos temporários desnecessários. Portanto, em vez de fazer com que o while
loop obtenha seu stdin tmpfile
, podemos fazer com que ele obtenha seu stdin de <(find . -name ${input} -print0)
.
A substituição de processos é amplamente útil. Em muitos lugares onde um comando deseja ler de um arquivo, você pode especificar a substituição de processo,, em <(...)
vez de um nome de arquivo. Há uma forma análoga,, >(...)
que pode ser usada no lugar do nome do arquivo onde o comando deseja gravar no arquivo.
Como os arrays, a substituição de processos é um recurso do bash e de outros shells avançados. Não faz parte do padrão POSIX.
Alternativa: lastpipe
Se desejado, lastpipe
pode ser usado em vez de substituição de processo (gorjeta: César ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
diz ao bash para executar o último comando no pipeline no shell atual (não no fundo). Dessa forma, o array
permanece após a conclusão do pipeline. Porque lastpipe
só tem efeito se o controle de trabalho estiver desativado, nós corremos set +m
. (Em um script, ao contrário da linha de comando, o controle de tarefas está desativado por padrão.)
Notas Adicionais
O comando a seguir cria uma variável de shell, não uma matriz de shell:
array=`find . -name "${input}"`
Se você quiser criar uma matriz, precisará colocar parênteses ao redor da saída de find. Então, ingenuamente, pode-se:
array=(`find . -name "${input}"`)
O problema é que o shell executa a divisão de palavras nos resultados de, de find
modo que os elementos do array não são garantidos como sendo o que você deseja.
Atualização 2019
A partir da versão 4.4-alpha, o bash agora suporta uma -d
opção para que o loop acima não seja mais necessário. Em vez disso, pode-se usar:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Para mais informações sobre este assunto, consulte (e upvote) a resposta de Benjamin W. .
IFS=
evitar a remoção de espaços em branco no início ou no final das linhas de entrada. Você pode testar isso facilmente comparando a saída deread var <<<' abc '; echo ">$var<"
com a saída deIFS= read var <<<' abc '; echo ">$var<"
. No primeiro caso, os espaços antes e depoisabc
são removidos. No último, eles não são. Nomes de arquivos que começam ou terminam com espaço em branco podem ser incomuns, mas, se existirem, queremos que sejam processados corretamente.<'
concluído <<(find aaa / -not -newermt "$ last_build_timestamp_v" -type f -print0) '''
pode ser usado em vez de$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
BLAH=$(find . -name '*.php')
. Conforme discutido na resposta, essa abordagem funcionará em casos limitados, mas não funcionará em geral com todos os nomes de arquivo e não produz, como o OP esperava, um array .O Bash 4.4 introduziu uma
-d
opção parareadarray
/mapfile
, então isso agora pode ser resolvido comreadarray -d '' array < <(find . -name "$input" -print0)
para um método que funciona com nomes de arquivos arbitrários, incluindo espaços em branco, novas linhas e caracteres globbing. Isso requer que o seu
find
suporte-print0
, como por exemplo o GNU find faz.No manual (omitindo outras opções):
E
readarray
é apenas sinônimo demapfile
.fonte
Se você estiver usando
bash
4 ou posterior, pode substituir o uso defind
porshopt -s globstar nullglob array=( **/*"$input"* )
O
**
padrão ativado porglobstar
corresponde a 0 ou mais diretórios, permitindo que o padrão corresponda a uma profundidade arbitrária no diretório atual. Sem anullglob
opção, o padrão (após a expansão do parâmetro) é tratado literalmente, portanto, sem correspondências, você teria uma matriz com uma única string em vez de uma matriz vazia.Adicione a
dotglob
opção à primeira linha também se quiser percorrer diretórios ocultos (como.ssh
) e combinar arquivos ocultos (como.bashrc
) também.fonte
nullglob
também ...dotglob
seja definido (isso pode ou não ser desejado, mas também vale a pena mencionar).você pode tentar algo como
, e para imprimir os valores do array, você pode tentar algo como echoarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
fonte
O seguinte parece funcionar para Bash e Z Shell no macOS.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
fonte
printf "%b" "file name with spaces, a star * ...\012and a second line\0" | xargs -0 touch
No bash,
$(<any_shell_cmd>)
ajuda a executar um comando e capturar a saída. Passar isso paraIFS
com\n
como delimitador ajuda a convertê-lo em uma matriz.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
fonte
find
array.Você poderia fazer assim:
#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done
fonte
Para mim, isso funcionou bem no cygwin:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
Isso funciona com espaços, mas não com aspas duplas (") nos nomes dos diretórios (que não são permitidos em um ambiente Windows de qualquer maneira).
Cuidado com o espaço na opção -printf.
fonte