Alguém pode esclarecer este script Bash?

3

Estou aprendendo algo sobre o script Bash, e me deparei com este exemplo:

A tarefa é escrever um script Bash que lê um arquivo, fornecido com o primeiro argumento, extrai os registros que possuem o mesmo ID que é fornecido com o segundo argumento e que residem no local fornecido com o terceiro argumento. / p>

Assim, o arquivo é uma lista de usuários, com informações sobre um usuário por linha, e é algo como isto (ID, nome, nome do pai, localização, telefone)

43  John  Mike  Smith  Boston  +3 685 123456

Após a "filtragem", eu tenho que gravar os dados encontrados na saída padrão, com as seguintes modificações: a primeira letra do local deve ser concatenada com o ID e, em seguida, o nome deve conter apenas a primeira letra do pai nome e um telefone sem nenhum prefixo.

A saída para o exemplo dado ficaria assim:

B43 John M Smith 123456

A solução é:

#!/bin/bash
cat $1|grep "^$2[0-9]*.*$3  +[0-9]*\ [0-9]*\ [0-9]*"|
sed "s/\([0-9]*\)\t\(.*\)\t\(.\).*\t\(.*\)\t\(.\).*\t+[0-9]*\ [0-9]*\
\([0-9]*\)/    /"

Eu não entendo qual é o sentido de | linhas verticais - Eu entendo que elas são pipes, e os dados de saída de uma 'query' servem como dados de entrada para outra 'query'. Por consulta quero dizer um comando shell.

Eu recebo a peça com o comando grep .

Eu não obtenho o comando sed . Como, exatamente, isso funciona? Como é que "sabe" colocar a primeira letra da localização no início da linha?

    
por idjuradj 19.01.2014 / 02:11

1 resposta

6

Explicação de linha por linha e pipe-a-pipe:

#!/bin/bash

Este é um chamado shebang - basicamente diz para executar este script com o programa /bin/bash .

cat $1

$1 é o primeiro argumento de script. cat $1 vai gerar o conteúdo do arquivo fornecido como primeiro argumento de script para stdout. No entanto, como há um pipe depois disso, o stdout será canalizado para stdin do próximo comando no pipeline, grep neste caso.

grep "^$2[0-9]*.*$3  +[0-9]*\ [0-9]*\ [0-9]*"

Isto irá ler o stdin (que cat $1 output acima). Você pode ler mais sobre grep, por exemplo, aqui:

O item acima filtrará as linhas fornecidas a ele por regex. O regex:

^$2[0-9]*.*$3  +[0-9]*\ [0-9]*\ [0-9]*

basicamente diz que queremos linhas que:

  • Comece com o segundo argumento de script ( $2 acima),
  • Em seguida, tenha zero ou mais dígitos ( [0-9]* )
  • Seguido por zero ou mais ocorrências de (quase) qualquer caractere
  • Seguido pelo terceiro argumento de script ( $3 )
  • Seguido por dois ou mais espaços ( + - observe que há dois espaços aqui)
  • Seguido de zero ou mais dígitos ( [0-9]* )
  • seguido por um espaço ( \ )
  • Seguido de zero ou mais dígitos ( [0-9]* )
  • seguido por um espaço ( \ )
  • Seguido de zero ou mais dígitos ( [0-9]* )

Todas as linhas que correspondem ao acima serão emitidas para stdout. Novamente, o stdout é canalizado para o stdin do próximo comando, sed neste caso.

sed "s/\([0-9]*\)\t\(.*\)\t\(.\).*\t\(.*\)\t\(.\).*\t+[0-9]*\ [0-9]*\
\([0-9]*\)/    /"

Você pode ler sobre sed mais, por exemplo, aqui:

O acima diz basicamente, para cada linha:

  • Substituir ( s/ )
  • (A) Isto: \([0-9]*\)\t\(.*\)\t\(.\).*\t\(.*\)\t\(.\).*\t+[0-9]*\ [0-9]*\([0-9]*\)
  • (B) Com isso:

A parte marcada com (A) acima é o regexp novamente, semelhante ao que o grep usou. O que diz é um pouco mais regular. Note que tem uma estrutura ao longo das linhas X\tY\tZ\t... . O que isso essencialmente diz para sed é - corresponde a uma linha que possui caracteres de tabulação (é isso que \t significa) e algumas coisas ( X , Y , Z ) entre elas. Essas coisas acima podem ser agrupadas de duas maneiras:

  • Expressões como \([0-9]*\) são chamadas de grupos de captura de expressão regular. Eles são basicamente delimitados por parênteses, exceto que sed é um pouco mais antigo do que a maneira usual de fazer em regexps hoje. Por exemplo. Se você usasse uma ferramenta de regex como o link , em vez disso, colocaria ([0-9]*) . sed precisa que estes sejam escapados para indicar grupos - caso contrário, seria necessário que correspondesse aos parênteses reais. Ele pode ser instruído a fazer o inverso fornecendo a opção de linha de comando -r
  • Expressões fora dos parênteses de escape (por exemplo, a parte \t+[0-9]* )

A captura de grupos é o que permite que sed faça o que você está pedindo. Observe a parte (B) do comando sed . Diz isso:

    

Esta é realmente uma boa maneira de dizer - substitua o que combinei nesta linha o quinto grupo de captura, depois o primeiro grupo, depois um espaço, depois um segundo grupo, etc.

Para deixar mais claro, aqui está um exemplo de comando para você tentar:

echo abc|sed 's/\(.\)\(.\)\(.\)//'

ou se você quiser em um formato de regex estendido que seja mais fácil de ler:

echo abc|sed -r 's/(.)(.)(.)//'

Execute-o e veja o que isso gera - observando que o eco gera três caracteres na linha e que para sed part . corresponde (quase) qualquer caractere, deve ficar claro o que e como isso se aplica à sua situação. Eu sugiro que você jogue com alguns exemplos de substituição de sed na Net - essa deve ser a melhor maneira de esclarecer as coisas.

Espero que isso ajude.

    
por 19.01.2014 / 03:00