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 / 679980
Accepted
Gabriel Staples
Gabriel Staples
Asked: 2021-12-03 22:15:54 +0800 CST2021-12-03 22:15:54 +0800 CST 2021-12-03 22:15:54 +0800 CST

Como processar individualmente cada caminho de uma lista de caminhos de saída do ripgrep

  • 772

Estou no Linux Ubuntu 18.04 e 20.04.

Ripgrep ( rg) pode gerar uma lista de caminhos para arquivos contendo correspondências como esta:

# search only .txt files
rg 'my pattern to match' -g '*.txt' -l
# long form
rg 'my pattern to match' --glob '*.txt' --files-with-matches

A saída será:

path/to/file1.txt
path/to/file2.txt
path/to/file3.txt

etc.

Eu gostaria de executar outro comando em cada caminho, como tree $(dirname $PATH), para obter uma lista de todos os arquivos no diretório que contém o arquivo correspondente. Como eu posso fazer isso?

Eu sinto que xargspode ser parte da resposta, talvez? Mas o pipe para xargscomeçar assim parece lidar apenas com o último arquivo impresso:

rg 'my pattern to match' -g '*.txt' -l | xargs -0 -I {} dirname {}

Nota: se você também puder fazer uma demonstração grep, isso também pode ser útil para aqueles sem ripgrep, embora o ripgrep seja super fácil de instalar .

Referências:

  1. ripgrep: imprime apenas nomes de arquivos correspondentes ao padrão
grep shell
  • 2 2 respostas
  • 169 Views

2 respostas

  • Voted
  1. Stéphane Chazelas
    2021-12-03T23:49:29+08:002021-12-03T23:49:29+08:00

    Em um sistema GNU, isso poderia ser assim:

    rg -g '*.txt' -l0 'my pattern to match' | # list files NUL-delimited
      xargs -r0 dirname -z -- |               # takes dirnames
      LC_ALL=C sort -zu |                     # remove duplicates
      xargs -r0 tree --
    

    Observe que, se ambos dir/file.txte dir/subdir/file.txtcorresponderem, você acabará executando treeem ambos dire dir/subdir, portanto, verá o conteúdo de dir/subdirduas vezes.

    Você teve a ideia certa usando xargsqual é o comando para converter uma string de bytes em uma lista de argumentos para passar para um comando e usar -0qual é a maneira mais confiável de passar uma lista arbitrária de argumentos, mas:

    • xargs -0espera a entrada em um formato onde a lista de argumentos é separada por caracteres NUL (0 bytes)¹. Você precisa da opção -0/ para imprimir a lista de arquivos nesse formato.--nullrg
    • GNU dirnamepode processar mais de um argumento por invocação, então ao invés de usar -I{}, nós apenas passamos todos eles². Também queremos -rnão invocar se a lista de arquivos estiver vazia, e a opção dirname(também específica do GNU) para imprimir os diretórios delimitados por NUL.-zdirnamedirname
    • como rgnão adiciona um ./prefixo a cada arquivo, é importante usar o --delimitador de opções para comandos para os quais passamos a lista de arquivos como argumentos para evitar problemas com -s iniciais nos nomes dos arquivos.

    Em resumo, para listas cujos valores podem ser qualquer sequência de bytes não-NUL, como caminhos de arquivo ou argumentos de comando arbitrários, você deseja usar registros delimitados por NUL como formato de intercâmbio, para passar listas programaticamente entre ferramentas e deixar apenas o formato humano para a ferramenta que dá feedback ao usuário (aqui a saída em forma de árvore de tree).


    Em um sistema não GNU, mas com o zshshell, você pode fazer:

    files=( ${(0)"(rg -g '*.txt' -l0 'my pattern to match')"} )
    typeset -U unique_dirs=( $files:h )
    (( $#unique_dirs )) && tree -- $dirs
    

    Ou de uma só vez (supondo que haja pelo menos um arquivo correspondente):

    tree -- ${(u)${(0)"$(rg -g '*.txt' -l0 'my pattern to match')"}:h}
    

    O u(para unique) é o que substitui typeset -U. O 0 sinalizador de expansão de parâmetro é como dizemos zshpara dividir em NULs. Alternativamente, poderíamos definir IFS=$'\0'e confiar na divisão de palavras (feita na expansão de parâmetros sem aspas) com:

    IFS=$'\0'
    tree -- ${(u)$(rg -g '*.txt' -l0 'my pattern to match'):h}
    

    Se você não tem utilitários GNU nem zsh, você sempre pode recorrer a perl:

    rg -g '*.txt' -l0 'my pattern to match' |
      perl -MFile::Basename -MList::Util=uniq  -0 -e '
        @dirs = uniq(map {dirname$_} <>);
        exec "tree", "--", @dirs if @dirs'
    

    ¹ esse é o único valor de caractere/byte que não pode ocorrer em um argumento de comando (já que os argumentos são passados ​​como strings delimitadas por NUL na execve()chamada do sistema), mas pode ocorrer em um fluxo de bytes alimentado por um pipe, portanto, é simples e óbvia maneira de separar argumentos arbitrários lá. -0é uma extensão não padrão da implementação GNU de xargs, mas agora é encontrada em muitas outras implementações

    ² ou pelo menos tantos quanto couber em uma invocação, chamando dirnamevárias vezes apenas se necessário.

    • 3
  2. Best Answer
    Gabriel Staples
    2021-12-04T10:58:31+08:002021-12-04T10:58:31+08:00

    ATUALIZAÇÃO: NOVA RESPOSTA FINAL:

    Observe que sort -zuclassifica e remove duplicatas em uma lista separada por nulos ( -z).

    rg 'my pattern to match' -0 -g '*.txt' -l \
    | sort -zu \
    | xargs -0 -I{} -- dirname {} \
    | xargs -0 -I{} -- tree {}
    

    DETALHES DA RESPOSTA ANTIGA:

    Veja os comentários abaixo desta resposta. Minha resposta aqui não é tão robusta quanto a outra resposta de @Stéphane Chazelas .

    Minha resposta abaixo originalmente não lidaria corretamente com nenhum nome de arquivo com espaços ou outro espaço em branco neles, nem lidaria com nomes de arquivo que começam com traço ( -). Aqui está o meu comentário de resposta abaixo:

    @StéphaneChazelas, todos os seus comentários fazem sentido. Sua resposta é mais robusta. Usar --null( -0) com rge com com xargscerteza seria mais robusto. Usando --também. Acho que não estava muito preocupado com essas coisas porque estou executando este comando em um repositório git onde nenhum arquivo tem espaços nem começa com traço ( -). Quanto às várias chamadas dirnamee treeem vez de uma chamada com vários caminhos, eu estava ciente disso, mas também estava bem com isso em parte porque queria uma resposta que pudesse expandir facilmente e adicionar mais tubos e comandos sem alterá-la drasticamente .

    Então, veja as duas respostas. O dele é tecnicamente melhor, mas para meus propósitos, o meu é "bom o suficiente" por enquanto e aponta que meu exemplo original na pergunta poderia ter funcionado com alterações super mínimas. Ex:

    # I should have done this (add `-0` to `rg` and add `--` to `xargs`):
    rg 'my pattern to match' -0 -g '*.txt' -l | xargs -0 -I {} -- dirname {}
    
    # instead of this:
    rg 'my pattern to match' -g '*.txt' -l | xargs -0 -I {} dirname {}
    

    A resposta de @Stéphane Chazelas e os comentários da minha pergunta ( incluindo um do próprio criador do ripgrep !)

    As strings do caminho de saída de rgNÃO são strings terminadas em nulo, portanto, remova -0do xargscomando (ou, inversamente, adicione-o ao rgcomando também). É isso! Isso agora funciona :

    # THESE WORK to get the dirnames!
    # (`--null`/`-0` are removed from both `rg` and `xargs`)
    
    rg 'my pattern to match' -g '*.txt' -l | xargs -I {} dirname {}
    # OR (same thing--remove the space after `-I` is all):
    rg 'my pattern to match' -g '*.txt' -l | xargs -I{} dirname {}
    

    OU, você pode forçar as strings de caminho a serem terminadas em nulo adicionando -0ou --nullao rgcomando, então isso também funciona:

    # ALSO WORKS
    # (`--null`/`-0` are ADDED to both `rg` and `xargs`; note that for
    # both `rg` and `xargs`, `--null` is the long form of `-0`)
    
    rg 'my pattern to match' -g '*.txt' -l --null | xargs --null -I{} dirname {}
    

    Agora, por extensão, podemos passar todos os caminhos para treetambém assim:

    RESPOSTA FINAL:

    rg 'my pattern to match' -0 -g '*.txt' -l \
    | xargs -0 -I{} -- dirname {} \
    | xargs -0 -I{} -- tree {}
    

    É isso! Eu simplesmente precisava adicionar ou subtrair -0 ou --nullde ambas rge todas as xargschamadas, para mantê-las consistentes e esperando os mesmos delineadores ao analisar os vários caminhos.

    Adicionar -0 ou --null, no entanto, é melhor, porque permite caminhos com espaços ou outros espaços em branco neles, e adicionar --também é bom porque permite caminhos que começam com traço ( -). Então, isso é o que eu fiz acima.

    Novamente, porém, veja a outra resposta também. Ele também classifica, remove duplicatas e lida com outras complexidades.

    Palavras-chave: como usar xargs corretamente; analisar caminhos de saída grep ou ripgrep rg com xargs

    • 0

relate perguntas

  • FreeBSD's sh: funções de lista

  • grep --line-buffered até X linhas?

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • o que grep -v grep faz

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

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