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 / 670006
Accepted
Kyu96
Kyu96
Asked: 2021-09-22 10:15:13 +0800 CST2021-09-22 10:15:13 +0800 CST 2021-09-22 10:15:13 +0800 CST

Atualizar itens fzf dinamicamente

  • 772

Estou escrevendo um script que procura arquivos no sistema e, em seguida, para cada arquivo, faz algumas verificações de sanidade e, se eles passarem, quero exibi-los em fzf. Quando o item é clicado em fzf, quero executar o arquivo com um programa.

Até agora tenho:

dir="/path/to/dir"

fd . $dir --size +1MB | while read -r line; do
    file_type=$(file -b "$line")
    echo "$line" | fzf
    if [[ "$file_type" == "data" ]]; then
        echo "$file_type"
    fi
done

Basicamente, procuro arquivos maiores que 1 MB no diretório especificado. Para cada arquivo eu executo o comando file e verifico se a saída do comando retorna "data". Se isso acontecer, quero adicioná-lo à lista fzf. Então, quando clico no item, quero executar o arquivo com o caminho completo com um aplicativo: por exemplo myapp /path/to/file/selected/in/fzf.

O problema até agora é que o fzf está bloqueando e não consigo preencher a lista no loop. Eu realmente tenho que adicionar tudo a uma matriz primeiro e depois canalizar isso para fzf? Idealmente, isso deve acontecer em paralelo, ou seja, quero adicionar novos itens rapidamente enquanto a pesquisa ainda está em andamento, em vez de esperar que ela seja concluída.

Também não sei como executar posteriormente o arquivo selecionado. alguém poderia me ajudar com isso?

bash shell
  • 1 1 respostas
  • 312 Views

1 respostas

  • Voted
  1. Best Answer
    Kamil Maciorowski
    2021-09-26T00:46:26+08:002021-09-26T00:46:26+08:00

    fzfnão bloqueia a maneira como você pensa. É totalmente capaz de adicionar à lista em tempo real. Você pode ver isso no exemplo a seguir (instale pvprimeiro se não estiver instalado):

    find / | pv -qlL 1 | fzf
    

    onde pvsai uma linha por segundo, as linhas aparecem em fzf, você pode mover a seleção para cima e para baixo e nada bloqueia.

    O problema com seu código é que você executa fzfdentro do while read …loop. Para cada linha, você executa um separado fzfque obtém apenas essa única linha. O loop pode continuar somente após o término fzf. Então não é que fzfse recusa a ler mais; é sobre fzfestar dentro do loop.

    Basicamente você quer algo assim:

    find … | data_filter | fzf
    

    onde data_filteré um pedaço de código que filtra linhas. Pode ser um loop de shell:

    find … | while IFS= read -r line; do …; done | fzf
    

    Como filtro, o loop deve imprimir em seu stdout todas as linhas que você não deseja filtrar (e apenas essas linhas). Pode ser uma função shell chamada data_filter. No seu caso, esta é a função correta:

    data_filter() {
       while IFS= read -r line; do
       [ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\n' "$line"
       done
    }
    

    E então você o usa como um filtro em um tubo.

    Agora temos o pipeline find … | data_filter | fzfque deve funcionar bem e imprimir qualquer nome de caminho que você escolher. Para fazer algo com o arquivo, use um destes:

    • find … | data_filter | fzf | xargs …, onde xargsestá configurado para ler as linhas completas como estão. Com GNU xargse se a ferramenta que você deseja executar for ls -l, ficaria assim:

      find … | data_filter | fzf | xargs --no-run-if-empty -d '\n' -I{} ls -l {}
      

      Mas como xargspor padrão interpreta aspas e faz outras coisas (como dividir), você precisa conhecer bem suas opções para usá-lo em um caso como este. Admito que nunca dominei xargse não tenho certeza se usei o melhor conjunto de opções aqui. Meu ponto é: uma simples invocação como … | fzf | xargs ls -lirá quebrar em muitos casos.

    • f="$(find … | data_filter | fzf)", depois use "$file"onde quiser. Uma vantagem é que você pode saber o status de saída do fzf. Uma desvantagem teórica é $()tirar novas linhas à direita. Na prática, no nosso caso, um nome de caminho com uma nova linha à direita (ou qualquer outra) não passa bem de data_filter | fzfqualquer maneira.

      Exemplo:

      f="$(find . | data_filter | fzf)"
      status="$?"
      [ "$status" = 0 ] && ls -l "$f"
      

    Como todas as ferramentas no pipeline usam novas linhas para separar entradas, os nomes de caminho com novas linhas quebrarão o código.


    Para lidar com nomes de caminho com novas linhas, você precisa fazer todo o pipeline funcionar com entradas terminadas em nulo (em oposição às terminadas em nova linha).

    • Primeiro de tudo, você precisa (ou comando find … -print0equivalente ). fdObserve algumas implementações de findnão suportam -print0( -exec printf '%s\0' {} +pode ser uma substituição).

    • Então a nova função de filtragem deve ser:

      data_filter0() {
         while IFS= read -r -d '' line; do
         [ "$(file -b "$line" 2>/dev/null)" = data ] && printf '%s\0' "$line"
         done
      }
      
    • Próxima utilização fzf --read0 --print0.

    • Finalmente xargs -r0 …. (A alternativa com $()é problemática e não vou elaborar; neste caso, prefira xargs -r0.) Observe que essas opções não são portáteis e você xargspode não suportá-las. Como um bônus xargs -r0funcionará bem com seleção múltipla de fzf -m.

    Um exemplo de pipeline:

    find . -print0 | data_filter0 | fzf -m --read0 --print0 | xargs -r0 ls -l
    

    Notas e links úteis:

    • Em geral, o filtro pode ser incorporado find …(graças a find -exec). Eu não fiz isso porque parece que você quer usar fde eu não conheço essa ferramenta bem o suficiente.

    • Se você escolher um item mais cedo (ou seja, antes finde o filtro terminar), então fzfe todos os itens posteriores no tubo podem sair antes das findsaídas. O filtro perceberá que fzfnão existe mais somente depois que tentar escrever; da mesma forma findnotará depois que tentar escrever (compare esta resposta ). Isso significa findque pode funcionar por mais tempo do que o necessário, impedindo que o shell passe para o próximo comando no script (ou exiba o prompt, se interativo). Você pode executar algumas partes em segundo plano:

      ( find . -print0 | data_filter0 & ) | fzf -m --read0 --print0 | xargs -r0 ls -l
      

      Isso não impedirá findnem o filtro de funcionar após as fzfsaídas, mas a linha inteira não irá parar. O shell seguirá em frente imediatamente após lsfazer seu trabalho. Os processos em segundo plano terminarão mais cedo ou mais tarde.

    • Cite certo . Na sua pergunta está sem aspas $dir.

    • Por que é printfmelhor do que echo? .

    • CompreensãoIFS= read -r line .

    • Como uso bytes nulos no Bash?

    • 1

relate perguntas

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • Como salvar um caminho com ~ em uma variável?

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

  • Execute um script muito lento até que seja bem-sucedido

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