Aqui está um script simples que está enrolando https://unix.stackexchange.com/ e armazenando o resultado em uma matriz, que está funcionando bem.
#!/usr/local/bin/bash
[ -f pgtoscrap ] && { rm pgtoscrap; };
curl -o pgtoscrap https://unix.stackexchange.com/;
declare -a arr;
fileName="pgtoscrap";
exec 10<&0
exec < $fileName
let count=0
while read LINE; do
arr[$count]=$LINE
((count++))
done
exec 0<10 10<&-
Mas, cada vez que eu executo este script; Eu recebo algum erro para o descritor de arquivo errado.
./shcrap
./shcrap: line 14: 10: No such file or directory
Acho que não entendo bem como usar o exec
comando em um loop corretamente. Alguém pode explicar?
-- Atualização após a implementação mapfile
para Bash 4 ficou muito mais simples --
#!/usr/local/bin/bash
## Pass a parameter as e.g. ./linkscrapping.bash https://unix.stackexchange.com/
mapfile -t arr < <(curl -s $1); ## Doing exec stuff with process substitution
regex="<a[[:print:]]*<\/a>"; ELEMENTS=${#arr[@]}; firstline=0;
for((i=0;i<$ELEMENTS;i++)); do
if [[ ${arr[${i}]} =~ $regex ]]; then
[[ $firstline<1 ]] &&
{ echo ${BASH_REMATCH[0]} > scrapped; let firstline=$firstline+1; } ||
{ echo ${BASH_REMATCH[0]} >> scrapped; }
fi
done
pg2scrap="scrapped"; mapfile -t arr2 < <(cat $pg2scrap);
regex="href=[\"\'][0-9a-zA-Z\:\/\.]+"; ELEMENTS2=${#arr2[@]}; line2=0
for ((i=0;i<$ELEMENTS2;i++)); do
if [[ ${arr2[${i}]} =~ $regex ]]; then
[[ $line2<1 ]] &&
{ echo ${BASH_REMATCH[0]#href=\"} > links; (( line2++ )); } ||
{ echo ${BASH_REMATCH[0]#href=\"} >> links; }
fi
done; cat links;
Certamente tem a ver com como você fecha o descritor de arquivo que você abriu anteriormente para stdin. Usando o abaixo deve ficar bem
Quando você faz
0<10
, você instrui o shell a procurar e slurp no conteúdo de um arquivo nomeado10
em seu diretório atual, o que não faz sentido nesse contexto.Em
bash
você também pode usar um formulário alternativoexec 10>&-
que atinge o mesmo objetivo de fechar o descritor.Mas dito isso, você não precisa usar
exec
um descritor de arquivo aleatório e ler sua entrada, você pode apenas ler sua entrada com a técnica de substituiçãobash
de processo no formato< <()
comoexec 10<&0
clona o descritor de arquivo número 0 para o número 10, salvando efetivamente o original para que você possa substituir o arquivo em fd 0 na próxima linha. Para desfazer isso, você precisaria reverter os números, clonar o número 10 para o número 0exec 0<&10
(e depois fechar o fd 10 comexec 10<&-
).Por outro lado,
exec 0<10
sem o e comercial é apenas um redirecionamento com um nome de arquivo10
. Como você não tem esse arquivo, você recebe um erro.Dito isso, você não precisa usar
exec
para configurar temporariamente um redirecionamento para o loop while. Comandos compostos também podem receber redirecionamentos, assim:Se você quiser ler as linhas completas como estão, sem espaços em branco ou barras invertidas afetando os dados, você precisa desdefinir
IFS
pararead
e usarread -r
. Além disso, se você estiver anexando a uma matriz, não precisará acompanhar manualmente os Ãndices, basta usar+=
para anexar diretamente à matriz:Ou use
mapfile
(readarray
) em vez de um loop manual como o @BlackJack menciona nos comentários:Ou mesmo sem um arquivo temporário:
(Sem
-t
,mapfile
deixa os terminadores de linha no lugar.)