Automatizando tubulações?

1

Eu me vejo usando basicamente a mesma linha várias vezes:

cat file | command1 | command2 | command3 > file

Existe uma maneira de colocar todos esses canais em um script, para que eu possa executar

automatic.sh file

e realizar a mesma coisa?

    
por Lucas Phillips 05.12.2013 / 20:33

3 respostas

4

Crie um arquivo com este conteúdo:

command1 | command2 | command3

Torne-o executável:

chmod +x that-file

E chame como:

/path/to/that-file < file.in > file.out

Adicione /path/to à sua variável $PATH para poder fazer:

that-file < file.in > file.out
    
por 05.12.2013 / 21:29
3

automatic.sh:

#!/bin/bash
cat $1 | command1 | command2 | command3 > .automatic.sh.temp
rm $1
mv .automatic.sh.temp $1

então chame como:

automatic.sh file

para usar um exemplo real:

arthur@a:~$ cat automatic.sh 
#!/bin/bash
cat $1 | grep foo | sed -e 's/foo/bar/g' | sort > $1

arthur@a:~$ cat <<END > foo
> 1 foo
> 2 bar
> 3 foo
> 4 bat
> 5 foo
> END
arthur@a:~$ chmod +x automatic.sh 
arthur@a:~$ ./automatic.sh foo
arthur@a:~$ cat foo
1 bar
3 bar
5 bar

E apenas para ser pedante, é uma forma um pouco melhor para gravar a saída em um arquivo temporário e, no final, mover o arquivo temporário sobre o arquivo original.

    
por 05.12.2013 / 20:35
1

Você pode criar um script ou função que contenha este comando. Use "$1 "para se referir ao primeiro argumento passado para o script ou função.

Há um grande erro no seu snippet de código: dependendo do tempo, > file pode truncar o arquivo antes que o primeiro comando no pipeline comece a ler o arquivo ou logo após a leitura. Seu snippet pode ocasionalmente funcionar com arquivos pequenos, mas na maioria das vezes não funcionará.

A maneira recomendada de modificar um arquivo é gravar em um novo arquivo temporário e, quando isso for concluído, mova-o para substituir a versão antiga. Dessa forma, se algo de ruim interromper o processamento (como um erro, uma falha de energia etc.), o arquivo antigo permanecerá em vigor.

Aqui está uma função que opera nesse princípio. Graças ao && após o pipeline, ele somente moverá o arquivo de saída para o local se command3 retornar um status de sucesso (observe que o status de retorno de outros comandos no pipeline é ignorado). Eu confio no utilitário comum mktemp para criar o arquivo temporário (ele garante que o nome do arquivo temporário não colidirá com qualquer outra instância do script ou qualquer outro programa).

my_pipeline () {
  out=$(TMPDIR=$(dirname -- "$1") mktemp)
  <"$1" command1 | command2 | command3 >"$out" &&
  mv -f "$out" "$1"
}

Coloque esta função no seu .bashrc ; Ele estará disponível na próxima vez que você começar o bash. Você também pode copiar e colar a definição na linha de comando para que ela tenha efeito nesse shell. Para usá-lo, digite o nome da função e, em seguida, o nome do arquivo para atuar:

my_pipeline my_file

Você pode fazer a função agir em todos os seus argumentos, por sua vez, colocando essas coisas em um loop.

my_pipeline () {
  for file; do
    out=$(TMPDIR=$(dirname -- "$file") mktemp)
    <"$file" command1 | command2 | command3 >"$out" &&
    mv -f "$out" "$file"
  done
}

Uso:

my_pipeline file1 file2 file3

Se você quiser criar um script, coloque o código em um arquivo que comece com uma linha shebang para indicar que é um script de shell.

#!/bin/sh
for file; do
  out=$(TMPDIR=$(dirname -- "$file") mktemp)
  <"$file" command1 | command2 | command3 >"$out" &&
  mv -f "$out" "$file"
done

Coloque o arquivo em seu caminho de busca de comando e torne-o executável (veja Como posso fazer um programa executável em qualquer lugar ).

Outra maneira de resolver o problema de truncar antes de usar é o utilitário esponja , mas esse utilitário não está disponível em todos os lugares (é do Debian).

    
por 06.12.2013 / 01:31