Uso transparente de arquivos temporários como meio de canalização [fechado]

3

Como muitos de nós estão certamente cientes, é sempre uma boa idéia fazer seu programa aceitar entrada de stdin. Muitos programas permitem esses ambientes * nix. Isso nos permite fazer coisas legais como canalizar %código%. Muitas vezes, pode-se descobrir que echo "foo" | less é logicamente equivalente a cat barfile | baz , pois nos bastidores é apenas leitura de strings de qualquer maneira.

Atualmente, existem muito mais programas que não podem ser canalizados por padrão. Alguns programas têm um sinalizador que ainda permite o comportamento mencionado acima, mas muitos não o fazem.

Agora, minha pergunta é: existe um pipe de arquivo temporário?

Agora, com minha capacidade literalmente inexistente de escrever Bash e cerca de 5 minutos no Google, eu criei este

#!/bin/bash
if [ $# -ne 2 ]; then exit 1; fi
f=$(mktemp)
($1) > $f
if [ $? -eq 0 ]; then ($2 $f); else exit $?; fi
rm $f

Chamando esse fpipe, podemos fazer coisas como baz barfile , em que baz é um programa que não podemos canalizar, mas que pode fazer fpipe 'wget -O- www.example.com' baz .

Minha pergunta é como podemos fazer melhor. Eu suspeito que com mais conhecimento de Bash, re-escrever o script acima para pegar qualquer quantidade de argumentos seria bastante trivial (para que possamos fazer coisas como baz file onde com a tubulação poderíamos fazer algo como fpipe 'foo x' bar baz . Bah, nós provavelmente poderia misturar os dois e acabar com coisas como foo x | bar | baz .

Existe uma construção existente que alcança isso? Eu acredito que eu vi uma construção de tipo fpipe 'wget -O- www.example.org | rev' baz ou algo ao longo destas linhas. Eu teria pensado que este é um problema bastante comum, mas minhas pesquisas não estão trazendo nada. Isso significa que ou eu não estou procurando bastante ou que estou perdendo algo bastante óbvio.

Se não houver um Proper Way (TM) para conseguir isso, é possível definir uma sintaxe de conveniência? Diga, foo x > bar < baz onde foo x |> baz | rev traduz diretamente para o meu script.

PS: Estou ciente de que meu / script / é muito frágil e ingênuo (como desistir de não 0); sinta-se à vontade para postar um melhor.

    
por Mateusz Kowalczyk 19.01.2013 / 22:11

3 respostas

4

Isso funcionará (em princípio) em qualquer shell, mas requer uma “versão recente suficiente” do sistema operacional:

wget -O- www.example.com | baz /dev/stdin

Quando existe /dev/stdin , normalmente é um link simbólico para /proc/self/fd/0 , por isso, se sua caixa não tiver /dev/stdin , verifique /proc/self/fd/0 .

Claro que a invenção dos tubos foi uma das grandes inovações do Unix. 1 Arquivos intermediários temporários são bons em princípio, mas pode ter custos de desempenho, e no caso de

program_that_produces_gobs_of_output | grep regular_expression_that_matches_very_few_lines

você provavelmente tem o problema de não conseguir ajustar o arquivo (toda a saída do primeiro comando) no disco. Então, o melhor dos dois mundos é usar um pipe nomeado. Isso permite capturar a saída padrão de algum programa 2 e fornecê-lo como entrada para outro programa como um parâmetro de nome de caminho:

f=$(mktemp)  &&  rm -f "$f"  &&  mkfifo "$f"
$1 >> "$f" &
$2 "$f"
rm -f "$f"

O primeiro rm -f "$f" é necessário porque mktemp não apenas gera um nome de arquivo; cria o arquivo. Se não removê-lo, o mkfifo falhará. (Como alternativa, poderíamos usar mktemp -u , que gera um nome de arquivo sem criar um arquivo.)

por 19.01.2013 / 22:45
5

Com uma versão recente do Bash:

$ baz <(wget -O- www.example.com)

Ela se chama substituição de processos .

    
por 19.01.2013 / 22:16
-1

Aqui estão dois métodos para que o seu script atue de forma idêntica se os argumentos do arquivo forem fornecidos, ou se ele deve ler as linhas do stdin:

1) cat os arquivos, use o nome de arquivo especial "-" se não houver arquivos especificados na linha de comando

#!/bin/bash
[[ $# -eq 0 ]] && set -- "-"
n=0
while read line; do
    echo "$((++n)) $line"
done < <(cat "$@")

2) se os arquivos forem especificados, coloque seu conteúdo no fluxo de stdin do script

#!/bin/bash
[[ $# -gt 0 ]] && exec 0< <(cat "$@")
n=0
while read line; do
    echo "$((++n)) $line"
done
    
por 20.01.2013 / 00:07