Como coletar corretamente uma matriz de linhas no zsh

39

Eu pensei que o seguinte agruparia a saída de my_command em uma matriz de linhas:

IFS='\n' array_of_lines=$(my_command);

para que $array_of_lines[1] se refira à primeira linha na saída de my_command , $array_of_lines[2] para a segunda e assim por diante.

No entanto, o comando acima não parece funcionar bem. Parece também dividir a saída de my_command em torno do caractere n , como verifiquei com print -l $array_of_lines , que, acredito, imprime elementos de uma matriz linha por linha. Eu também verifiquei isso com:

echo $array_of_lines[1]
echo $array_of_lines[2]
...

Numa segunda tentativa, pensei que adicionar eval poderia ajudar:

IFS='\n' array_of_lines=$(eval my_command);

mas eu tenho exatamente o mesmo resultado que sem ele.

Por fim, seguindo a resposta em Listar elementos com espaços em zsh , Eu também tentei usar sinalizadores de expansão de parâmetro em vez de IFS para dizer ao zsh como dividir a entrada e coletar os elementos em uma matriz, ou seja:

array_of_lines=("${(@f)$(my_command)}");

Mas ainda tenho o mesmo resultado (a divisão acontece em n )

Com isso, tenho as seguintes perguntas:

Q1. Quais são as formas "apropriadas" de coletar a saída de um comando em uma matriz de linhas?

Q2. Como posso especificar IFS para dividir somente em novas linhas?

Q3. Se eu usar sinalizadores de expansão de parâmetro como na minha terceira tentativa acima (ou seja, usando @f ) para especificar a divisão, zsh ignorará o valor de IFS ? Por que não funcionou acima?

    
por Amelio Vazquez-Reina 22.01.2012 / 20:20

4 respostas

62

Primeiro erro (→ Q2): IFS='\n' define IFS para os dois caracteres \ e n . Para definir IFS para uma nova linha, use IFS=$'\n' .

Segundo erro: para definir uma variável como um valor de matriz, você precisa de parênteses em torno dos elementos: array_of_lines=(foo bar) .

Isso funcionaria:

IFS=$'\n' array_of_lines=($(my_command))

Mas eu recomendo não mexer com IFS ; em vez disso, use o sinalizador de expansão f para dividir em novas linhas (→ Q1):

array_of_lines=("${(@f)$(my_command)}")

O valor de IFS não importa lá. Eu suspeito que você usou um comando que divide em IFS para imprimir $array_of_lines em seus testes (→ Q3).

    
por 23.01.2012 / 02:01
4

Duas questões: primeiro, aparentemente aspas duplas também não interpretam escapes de barra invertida (desculpem por isso :). Use $'...' quotes. E, de acordo com man zshparam , para coletar palavras em uma matriz, você precisa colocá-las entre parênteses. Então, isso funciona:

% touch 'a b' c d 'e f'
% IFS=$'\n' arr=($(ls)); print -l $arr
a b
c
d
e f
% print $arr[1]
a b

Eu não posso responder ao seu Q3. Espero que nunca tenha que conhecer essas coisas esotéricas:).

    
por 23.01.2012 / 01:09
0

Você também pode usar tr para substituir a nova linha por espaço:

lines=($(mycommand | tr '\n' ' '))
select line in lines; do
  echo $line
  break
done
    
por 21.10.2016 / 18:25
-1

zsh me deixou louco quando tentei usá-lo, então parei de usá-lo.

Dito isto, aqui está como eu faria o que você está tentando fazer. O zsh trata a nova linha "especialmente" no IFS, portanto você deve evitá-lo. Substitua algum outro caractere no lugar de nova linha e o IFS funciona principalmente como você esperaria. Portanto, se você tiver certeza de que my_command nunca emita esse caractere, poderá usá-lo como um separador de linha substituto. No código de exemplo abaixo eu uso ASCII VT ou control-K (octal 013) para este propósito.

vt='echo foo | tr -s -c '3' '3''
IFS="$vt"
set -A array_of_lines $(my_command | tr '2' '3')
unset IFS

A primeira linha coloca o caractere VT na variável vt. A segunda linha configura o IFS para conter apenas o VT. A terceira linha executa my_command e, em seguida, converte todas as novas linhas de saída em VTs com o comando tr . Em seguida, set divide a saída em parâmetros separados e armazena-os em array_of_lines. A quarta linha redefine o IFS, o que faz com que o zsh se comporte como se o IFS estivesse configurado para seu valor padrão.

Definindo o IFS e chamando set na mesma linha como esta

IFS="$vt" set -A array_of_lines $(my_command | tr '2' '3')

não funciona. Não me pergunte por quê; Eu odeio zsh.

    
por 22.01.2012 / 23:47