Capture em variáveis shell os argumentos depois de um comando (entre parênteses)

5

Suponha que eu tenha um arquivo que contenha, entre muitas outras coisas,

\command{arg1,arg2,arg3}

(argumentos foram caminhos, expressos com / , . , caracteres e números)

Mas que um usuário também pode chamá-lo com

\command{arg1,
arg2 ,
arg3
}

Ou seja, em várias linhas e com espaços supérfluos.

Gostaria de encontrar um padrão regular para incluir em um script de shell, para que n variáveis contenham n argumentos. Como proceder?

Eu consegui escrever

echo "\command{arg1,
    arg2 ,
    arg3
    }" | sed -n -e 's/\command//p' | sed 's/,/\n/' | sed 's/{\|}//'

mas isso só gera arg1 , e eu nem tenho certeza sobre como armazená-lo em uma variável.

Relacionados:

Mas eu não fui capaz de combinar todos esses ingredientes para conseguir o que eu quero.

    
por Clément 31.01.2015 / 00:16

2 respostas

5

I'd like to find a regular pattern to include in a shell script so that n variables will contain the n arguments

O seguinte cria uma matriz de shell arglist que contém cada um dos argumentos:

$ readarray -t arglist < <(echo "\command{arg1,
    arg2 ,
    arg3
    }" | sed -n '/\command/{ :a;/}/!{N;b a}; s/\command{//; s/[ \n}]//g; s/,/\n/g; p}')

Usando a declaração declare , podemos ver que funcionou:

$ declare -p arglist
declare -a arglist='([0]="arg1" [1]="arg2" [2]="arg3")'

Aqui está outro exemplo com os argumentos em uma linha:

$ readarray -t arglist < <(echo "\command{arg1, arg2, arg3, arg4}"  | sed -n '/\command/{ :a;/}/!{N;b a}; s/\command{//; s/[ \n}]//g; s/,/\n/g; p}')

Mais uma vez, funciona:

$ declare -p arglist
declare -a arglist='([0]="arg1" [1]="arg2" [2]="arg3" [3]="arg4")'

Observe que o espaço em < <( é essencial. Estamos redirecionando a entrada de uma substituição de processo. Sem o espaço, bash tentará algo totalmente diferente.

Como funciona

O comando sed é um pouco sutil. Vamos ver um pedaço de cada vez:

  • -n

    Não imprima linhas, a menos que explicitamente solicitado.

  • /\command/{...}

    Se encontrarmos uma linha que contenha \command , execute os comandos encontrados nas chaves, que são os seguintes:

  • :a;/}/!{N;b a}

    Isto lê as linhas no buffer padrão até encontrarmos uma linha que contenha } . Desta forma, obtemos o comando inteiro de uma vez.

  • s/\command{//

    Remova a string \command{ .

  • s/[ \n}]//g

    Remova todos os espaços, fechando chaves e novas linhas.

  • s/,/\n/g

    Substitua vírgulas por novas linhas. Quando isso é feito, cada argumento está em uma linha separada, que é o que readarray quer.

  • p

    Imprimir.

por 31.01.2015 / 00:38
3

com perl :

perl -l -0777 -ne '
  $n = 0;
  for (/\command\{\s*(.*?)\s*\}/sg) {
    $n++;
    $i = 0;
    for $arg (split /\s*,\s*/, $_) {
      $arg =~ s/'\''/$&\$&$&/g;
      print "arg${n}[$i]='\''$arg'\''";
      $i++;
    }
  }
  print "n=$n"' the-file

Produziria algo como:

arg1[0]='arg1'
arg1[1]='arg2'
arg1[2]='arg3'
n=1

Qual você poderia avaliar como:

eval "$(perl ...)"

para criar $arg1 , $arg2 ... matrizes para cada um dos \command s.

    
por 31.01.2015 / 00:26