AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 558773
Accepted
Toothrot
Toothrot
Asked: 2019-12-25 05:24:39 +0800 CST2019-12-25 05:24:39 +0800 CST 2019-12-25 05:24:39 +0800 CST

Dobrando um pipeline de volta à sua origem

  • 772

Alguém poderia pensar que

echo foo >a
cat a | rev >a

deixaria acontendo oof; mas em vez disso é deixado vazio.

  1. Por quê?
  2. Como se aplicaria revde outra forma a?
pipe io-redirection
  • 7 7 respostas
  • 1980 Views

7 respostas

  • Voted
  1. Best Answer
    terdon
    2019-12-25T05:53:51+08:002019-12-25T05:53:51+08:00

    Existe um aplicativo para isso! O spongecomando from moreutilsé projetado exatamente para isso. Se você estiver executando o Linux, provavelmente já está instalado, caso contrário, procure nos repositórios do seu sistema operacional spongeou moreutils. Então, você pode fazer:

    echo foo >a
    cat a | rev | sponge a
    

    Ou, evitando o UUoC :

    rev a | sponge a
    

    A razão para esse comportamento está na ordem em que seus comandos são executados. O > aé realmente a primeira coisa executada e > fileesvazia o arquivo. Por exemplo:

    $ echo "foo" > file
    $ cat file
    foo
    $ > file
    $ cat file
    $
    

    Então, quando você executa cat a | rev >ao que realmente acontece é que o > aé executado primeiro, esvaziando o arquivo, então quando o cat aé executado o arquivo já está vazio. Justamente por isso spongefoi escrito (de man sponge, grifo meu):

    O esponja lê a entrada padrão e a grava no arquivo especificado. Ao contrário de um redirecionamento de shell, o esponja absorve toda a sua entrada antes de gravar o arquivo de saída. Isso permite a construção de pipelines que lêem e gravam no mesmo arquivo.

    • 33
  2. stolenmoment
    2019-12-25T05:40:35+08:002019-12-25T05:40:35+08:00
    1. O truncamento de saída é feito muito cedo, então cat vê um arquivo vazio.
    2. Ou o primeiro arquivo é construído como temporário ou a saída de rev é direcionada para um temporário que você renomeia.
    • 11
  3. Jasen
    2019-12-26T02:27:27+08:002019-12-26T02:27:27+08:00

    outra maneira de corrigir isso é usar um método de escrita que não trunque

      rev a | dd conv=notrunc of=a
    

    isso só funciona porque:

    1. rev lê o conteúdo antes de produzir a saída e a saída nunca é maior do que a quantidade já lida

    2. o novo conteúdo do arquivo é do mesmo tamanho ou maior que o original (neste caso, mesmo tamanho)

    3. dd abre o arquivo para escrever sem truncá-lo.

    Essa abordagem pode ser útil para modificação no local de arquivos muito grandes para manter cópias temporárias.

    • 9
  4. ilkkachu
    2019-12-26T12:51:21+08:002019-12-26T12:51:21+08:00
    cat a | rev > a
    

    Por que [é adeixado vazio]?

    No pipeline acima, o shell bifurca dois subprocessos, um para cada uma das duas partes do pipeline. Esses subprocessos executam os comandos em questão, primeiro processando qualquer redirecionamento e, em seguida, chamando uma das exec*()funções para iniciar o utilitário externo. Os subprocessos são executados em paralelo e não há garantias de tempo entre eles .

    A execução de um processo não é muito rápida, então geralmente o que acontece é que o shell do lado direito consegue configurar o redirecionamento antes catde ter a chance de ler o arquivo. O redirecionamento de saída > atrunca o arquivo, portanto cat, não tem nada para ler, revnão recebe dados e não produz dados. Mesmo se você usar um redirecionamento do lado esquerdo também ( cat < a | rev > a), apode ser aberto para leitura antes de ser truncado, mas catainda assim provavelmente não teria tempo para lê-lo antes disso.

    Por outro lado, isso é impresso de forma bastante consistente a contains: foono meu sistema:

    echo foo > a; cat < a | tee a > /dev/null ; echo "a contains: $(cat a)"
    

    Aqui é teeque trunca o arquivo, então isso acontece depois do exec()e cattem mais chance de ter tempo de ler o arquivo. Mas se o arquivo for grande o suficiente, ele pode ficar truncado no meio da leitura.

    Eu disse pode e provavelmente existe, porque de fato o oposto pode acontecer, se o SO decidir agendar os processos de outra forma.

    Como se aplicaria revde outra forma a?

    A solução usual é usar um arquivo temporário:

    cat a | rev > b && mv b a
    

    Embora haja o problema usual de possivelmente substituir um arquivo existente, a menos que você possa ter certeza de que o nome do arquivo temporário está disponível. Você provavelmente deve usar mktemp:

    f=$(mktemp ./tmp.XXXXXX)
    cat a | rev > "$f" && mv "$f" a || rm "$f"
    

    Como alternativa, você pode usar a spongeferramenta , que garante a leitura de todas as entradas recebidas antes de abrir o arquivo de saída (caso contrário, é como cat):

    cat a | rev | sponge a
    

    ou apenas

    rev < a | sponge a
    

    sponge > aseria um erro pela mesma razão que o comando original não funciona.


    A esponja é de moreutils e não uma ferramenta padrão. Algumas alternativas para ele estão listadas em Completamente buffer de saída do comando antes de canalizar para outro comando?

    Alguns utilitários podem implementar um recurso semelhante, por exemplo, sort -o outputfilesó abre o arquivo de saída após terminar, veja O sort suporta a ordenação de um arquivo no local, como `sed --in-place`?

    • 4
  5. ikegami
    2020-05-14T21:14:39+08:002020-05-14T21:14:39+08:00

    >filecria um novo arquivo vazio ou trunca um arquivo existente. Como tal, não há mais nada no arquivo para revler.


    Como outras respostas mencionaram, você pode usar spongepara isso. Mas spongenão está disponível para todos.

    O seguinte é uma solução genérica que envolve apenas o shell:

    exec 3<file; rm file; rev <&3 >file; exec 3<&-
    

    Isso abre o arquivo (como fd 3) e o exclui. Mais precisamente, isso exclui apenas a entrada de diretório do arquivo, não o arquivo em si. O arquivo não será excluído até que todos os links físicos sejam excluídos e todos os identificadores sejam fechados.

    Em seguida, revé executada a leitura do "arquivo excluído". Sua saída é enviada para um novo arquivo. Embora esse novo arquivo tenha o mesmo nome do arquivo original, é um arquivo diferente. Como tal, não há conflito.

    Por fim, fechamos o descritor para o arquivo original, permitindo que ele seja liberado.


    O problema com a abordagem acima é que os dados são perdidos se ocorrer um problema. É por isso que se pode preferir o seguinte (que não usa mais espaço em disco do que o acima):

    ( rev file >file.new && mv file.new file ) || rm file.new
    
    • 1
  6. Zombo
    2019-12-28T10:16:34+08:002019-12-28T10:16:34+08:00

    Você pode usar o Vim no modo Ex:

    ex -s -c '%!rev' -c x a.txt
    
    • %selecione todas as linhas
    • !comando de execução
    • xsalvar e fechar
    • 0
  7. tinnick
    2020-05-14T21:53:58+08:002020-05-14T21:53:58+08:00

    As razões foram explicadas por outros, mas basicamente a mesma razão pela qual você não pode fazer:

    rev a >a
    

    Mas como alguém pode conseguir isso:

    echo foo >a
    echo `cat a | rev` >a
    # or
    echo `rev a` >a
    
    • 0

relate perguntas

  • Como salvar uma saída do airodump-ng em um arquivo?

  • Passe o identificador para o pipeline stdin down

  • Como canalizar um comando bash e manter Ctrl + C funcionando?

  • Por que canalizar `mysql` para 'tail' altera o formato de saída?

  • ordem de substituição de processos `te` e `bash`

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve