Considere este script:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Isso funciona e gera:
line 3
line 1
line 2
line 3
Digamos que nossa fonte de entrada, em vez de ser um arquivo real, fosse stdin:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
Como podemos modificar o comando:
cat <(tail -1 "$tmpfile") "$tmpfile"
Para que ainda produza a mesma saída, neste contexto diferente?
NOTA: O Heredoc específico que estou criando, bem como o uso de um Heredoc em si, são meramente ilustrativos. Qualquer resposta aceitável deve assumir que está recebendo dados arbitrários via stdin .
Respostas:
Tentar:
Exemplo
Defina uma variável com a nossa entrada:
Execute nosso comando:
Como alternativa, é claro, poderíamos usar um documento aqui:
Como funciona
x=x $0 ORS
Isso acrescenta cada linha de entrada à variável
x
.No awk,
ORS
é o separador de registros de saída . Por padrão, é um caractere de nova linha.END{printf "%s", $0 ORS x}
Depois que lemos o arquivo inteiro, isso imprime a última linha
$0
, seguida pelo conteúdo do arquivo inteirox
,.Como isso lê toda a entrada na memória, não seria apropriado para entradas grandes ( por exemplo, gigabytes).
fonte
tee
que, mas de um stdin e de um arquivo, estaríamos canalizando o mesmo stdin em duas substituições de processo diferentes. ou qualquer coisa que seria aproximadamente equivalente a isso?Se o stdin apontar para um arquivo que pode ser procurado (como no caso dos documentos do bash (mas não de todos os outros shell) aqui implementados com arquivos temporários), você pode obter a cauda e procurar novamente antes de ler o conteúdo completo:
procurar operadores estão disponíveis nos
zsh
ouksh93
conchas, ou linguagens de script como tcl / perl / python, mas não embash
. Mas você sempre pode ligar para intérpretes mais avançadosbash
se precisar usarbash
.Ou
Agora, isso não funcionará quando o stdin apontar para arquivos não procuráveis, como um cano ou soquete. Então, a única opção é ler e armazenar (na memória ou em um arquivo temporário ...) toda a entrada.
Algumas soluções para armazenar na memória já foram fornecidas.
Com um arquivo temporário, com
zsh
, você poderia fazê-lo com:Se no Linux, com
bash
ouzsh
ou qualquer shell que use arquivos temporários para documentos aqui, você poderá realmente usar o arquivo temporário criado por um documento aqui para armazenar a saída:fonte
O problema de traduzir isso para algo que usa
tail
é que vocêtail
precisa ler o arquivo inteiro para encontrar o final dele. Para usar isso em seu pipeline, você precisatail
.cat
.A parte complicada não é duplicar o conteúdo do documento (
tee
isso é possível), mas fazer com que a saídatail
ocorra antes da saída do restante do documento, sem usar um arquivo temporário intermediário.O uso
sed
(ouawk
, como John1024 faz ) se livra da análise dupla dos dados e do problema de pedido armazenando os dados na memória.A
sed
solução que proponho é1{h;d;}
, armazene a primeira linha no espaço em espera, como está, e pule para a próxima linha.H
, acrescente uma à outra linha ao espaço de espera com uma nova linha incorporada.${G;p;}
, acrescente o espaço de espera à última linha com uma nova linha incorporada e imprima os dados resultantes.Esta é uma tradução literal da solução de John1024
sed
, com a ressalva de que o padrão POSIX apenas garante que o espaço de espera seja de no mínimo 8192 bytes (8 KiB; mas recomenda que esse buffer seja alocado e expandido dinamicamente conforme necessário, que ambos GNUsed
e BSDsed
está fazendo).Se você se permitir usar um pipe nomeado:
Isso usa
tee
para enviar os dados para baixomypipe
e ao mesmo tempo paracat
. Ocat
utilitário primeiro lerá a saída detail
(que lê demypipe
, para o qualtee
está gravando) e, em seguida, anexará a cópia do documento proveniente diretamentetee
.Porém, há uma falha séria nisso: se o documento for muito grande (maior que o tamanho do buffer do tubo), ele estará
tee
gravandomypipe
ecat
bloqueando enquanto aguarda que o tubo (sem nome) seja esvaziado. Não seria esvaziado até sercat
lido.cat
não leria atétail
terminar. Etail
não iria terminar atétee
terminar. Essa é uma situação clássica de conflito.A variação
tem o mesmo problema.
fonte
sed
um não funciona se a entrada tem apenas uma linha (talvezsed '1h;1!H;$!d;G'
). Observe também que váriassed
implementações têm um limite baixo no tamanho de seu padrão e mantêm espaço.Existe uma ferramenta nomeada
pee
em uma coleção de utilitários de linha de comando geralmente empacotados com o nome "moreutils" (ou recuperável no site da Web ).Se você pode tê-lo em seu sistema, o equivalente ao seu exemplo seria:
A ordem dos comandos executados
pee
é importante porque eles são executados na sequência fornecida.fonte
Tentar:
Como a coisa toda são dados literais (um "aqui está o documento") e a diferença entre eles e a saída desejada é trivial, basta massagear esses dados literais ali mesmo para corresponder à saída.
Agora, suponha que
line 3
venha de algum lugar e seja armazenado em uma variável chamadalastline
:Em um documento aqui, podemos gerar texto substituindo variáveis. Não apenas isso, mas podemos calcular o texto usando a substituição de comandos:
Podemos interpolar várias linhas:
Em geral, evite o processamento de texto no modelo de documento aqui; tente gerá-lo usando código interpolado.
fonte
cat <<EOS...
no OP foi apenas um exemplo para "criar um arquivo arbitrário", para tornar o post específico e a pergunta clara. Isso realmente não era óbvio para você, ou você apenas pensou que seria inteligente interpretar a pergunta literalmente?Se você não se importa com o pedido. Então isso vai funcionar
cat lines | tee >(tail -1)
. Como outros já disseram. Você precisa ler o arquivo duas vezes ou armazenar em buffer o arquivo inteiro na ordem solicitada.fonte