Eu estava tentando criar uma função que faz um loop nas entradas e executa um comando - independentemente de como elas são delimitadas.
function loop {
# Args
# 1: Command
# 2: Inputs
for input in "$2" ; do
$1 $input
done
}
declare -a arr=("1" "2" "3")
$ loop echo "$arr[@]"
1
$ loop echo 1 2 3
1
$ loop echo $arr
1
No entanto, de acordo com esta resposta, for .. in ..
funciona para matrizes:
for item in "${arr[@]}" ; do
echo "$item"
done
Também funciona para valores separados por espaço:
for item in 1 2 3 ; do
echo "$item"
done
Em poucas palavras, como obtenho o efeito de "${arr[@]}"
e 1 2 3
ao passar um argumento.
Também seria possível estender essa noção de loop para qualquer tipo de itens delimitados, por exemplo, \n
conteúdos separados como um arquivo? Em Python temos um conceito de iterators
, existe algo parecido no bash?
Você não chamou a matriz corretamente.
$arr
expandirá apenas para o primeiro elemento na matriz e$arr[@]
expandirá para o primeiro elemento com a string literal[@]
anexada a ele.Para chamar todos os elementos de um array use:
"${arr[@]}"
O outro problema que você tem é que
$2
contém apenas o segundo parâmetro posicional, onde você está tentando iterar pelo 3º, 4º, 5º, etc. Todos eles serão armazenados em$@
.Para atingir seu objetivo, você pode fazer algo como:
Isso definirá
command
o primeiro parâmetro posicional e, em seguida, mudará para que$@
possa ser usado para percorrer os restantes. Então você só precisa chamar o array corretamente:Se você quiser que esta função aceite vários delimitadores, você pode fazer algo como:
Isso significa que você precisa especificar qual delimitador está sendo usado por meio do segundo parâmetro, como:
No entanto, isso tem alguns bugs (se você especificar um delimitador personalizado, ele também delimitará por espaço em branco)
Você tem um erro de sintaxe em seu código. Na chamada para sua
loop
função, use"${arr[@]}"
para expandir oarr
array para a lista de cada um de seus elementos, citados individualmente. Isso é o que você faz nofor
loop que você mostra, e isso também é o que você deve fazer ao chamar a função:Observe também que sua função precisa escolher o nome do comando que você passa e que precisa fazer um loop sobre seus argumentos restantes:
Aqui, atribuímos o primeiro argumento à variável
cmd
(este é o comando), depois retiramosshift
esse argumento da lista de argumentos. A lista de argumentos agora contém apenas as strings sobre as quais você deseja fazer um loop.O loop então chama o comando para cada argumento restante.
Isso replica o propósito do
xargs
utilitário de maneira limitada, e a função pode ser reimplementada usando este utilitário (a menos que você espere que um dos argumentos contenha uma nova linha incorporada, caso em que você terá que ajustar as opções paraxargs
) :Como o
xargs
utilitário espera que os dados sejam entregues pela entrada padrão, combinamos com isso usandoprintf
e imprimimos cada argumento fornecidoloop
em uma linha própria (enquanto informamosxargs
para chamar o utilitário fornecido uma vez para cada argumento delimitado por nova linha usando-L 1
).Isso nos permitiria usar outros recursos de algumas implementações de
xargs
, como iniciar processos paralelos usando-P n
wheren
is um certo número de processos a serem executados em paralelo.Teste:
Seu loop é muito semelhante ao GNU Parallel:
-j1
força a execução de um único trabalho por vez.