Os xargs usam alias em vez de binários


13

Bash 4.2 no CentOS 6.5:

No meu ~/.bash_profileeu tenho um monte de aliases, incluindo:

alias grep='grep -n --color=always'

para que eu possa obter destaque de cores e imprimir números de linha automaticamente ao executar grep. Se eu executar o seguinte, o destaque funcionará conforme o esperado:

$ grep -Re 'regex_here' *.py

No entanto, quando eu executei isso recentemente:

$ find . -name '*.py' | xargs grep -E 'regex_here'

os resultados não foram destacados e os números de linha não foram impressos, forçando-me a voltar e adicionar explicitamente -n --color=alwaysao grepcomando.

  • Não xargslê aliases no ambiente?
  • Se não, existe uma maneira de fazer isso?


@psimon certo, isso significa dizer para fazer o que já fiz na minha solução alternativa - tive que expandir manualmente meu apelido no xargscomando. O que estou tentando descobrir é se existe uma maneira de chamar diretamente meu apelido xargs.
— 8894 MattelData de publicação

1
Você já tentou export GREP_OPTIONS='-n --color=always'antes do seu comando xargs?
— doneal24

@ DougO'Neal obrigado, isso funcionou! Vou acrescentar isso ao meu .bash_profile. Sinta-se livre para escrever uma resposta ...
— MattDMo

Respostas:


10

Um alias é interno ao shell em que está definido. Não é visível para outros processos. O mesmo vale para funções de shell. xargsé um aplicativo separado, que não é um shell, portanto não possui um conceito de alias ou funções.

Você pode fazer xargs chamar um shell em vez de chamar grepdiretamente. No entanto, apenas invocar um shell não é suficiente, você precisa definir o alias nesse shell também. Se o alias estiver definido no seu .bashrc, você pode obter esse arquivo; no entanto, isso pode não funcionar, você .bashrcrealiza outras tarefas que não fazem sentido em um shell não interativo.

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E regex_here "$@"' _

Cuidado com os meandros da cotação aninhada ao digitar a regexp. Você pode simplificar sua vida passando o regexp como um parâmetro para o shell.

find . -name '*.py' | xargs bash -c '. ~/.bashrc; grep -E "$0" "$@"' regex_here

Você pode executar a pesquisa de alias explicitamente. Então xargsvai ver grep -n --color=always.

find . -name '*.py' | xargs "${BASH_ALIASES[grep]}" regex_here

No zsh:

find . -name '*.py' | xargs $aliases[grep] regex_here

A propósito, observe que find … | xargs … quebras nos nomes de arquivos que contêm espaços (entre outros) . Você pode corrigir isso alterando para registros delimitados por nulo:

find . -name '*.py' -print0 | xargs -0 "${BASH_ALIASES[grep]}" regex_here

ou usando -exec:

find . -name '*.py' -exec "${BASH_ALIASES[grep]}" regex_here {} +

Em vez de ligar find, você pode fazer tudo inteiramente dentro do shell. O padrão glob **/percorre diretórios recursivamente. No bash, você precisa executar shopt -s globstarpara ativar esse padrão glob primeiro.

grep regex_here **/*.py

Isso tem algumas limitações:

  • Se muitos arquivos corresponderem (ou se tiverem caminhos longos), o comando poderá falhar porque excede o comprimento máximo da linha de comando.
  • No bash ≤4.2 (mas não nas versões mais recentes, nem no ksh ou no zsh), ocorre novamente **/em links simbólicos para diretórios.

Outra abordagem é usar a substituição de processos, conforme sugerido por MariusMatutiae .

grep regex_here <(find . -name '*.py')

Isso é útil quando **/não é aplicável: para findexpressões complexas ou no bash ≤4.2 quando você não deseja se recuperar sob links simbólicos. Observe que isso quebra nos nomes de arquivos que contêm espaços; uma solução alternativa é definir IFSe desativar o globbing , mas está começando a ficar um pouco complexo:

(IFS=$'\n'; set -f; grep regex_here <(find . -name '*.py') )

obrigado pela explicação clara de por que aliases não são visíveis para outros processos
— MattDMo

Pode-se também usar substituição de processo, veja minha resposta.
— MariusMatutiae

11

Usar alias xargs='xargs '

alias: alias [-p] [name[=value] ... ]
(snip)
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.

Obrigado por isso, eu não sabia sobre o truque espacial à direita.
— precisa saber é o seguinte

Np. Também é útil com sudo…
— 1.61803 21/11/2015

2

Por favor, tome isso como uma demonstração de outra abordagem, que não consigo encontrar no pergunta SO :

Você pode escrever uma função de invólucro para xargs qual verifica se o primeiro argumento é um alias e, se houver, expanda-o de acordo.

Aqui está um código que faz exatamente isso, mas infelizmente requer o shell Z e, portanto, não roda 1: 1 com o bash (e, francamente, eu não estou acostumado com o bash o suficiente para portá-lo):

xargs () {
        local expandalias
        if [[ $(which $1) =~ "alias" ]]; then
                expandalias=$(builtin alias $1) 
                expandalias="${${(s.'.)expandalias}[2]}"
        else
                expandalias=$1
        fi
        command xargs ${(z)expandalias} "${(z)@[2,-1]}"
}

Prova de que funciona:

zsh%  alias grep = "grep -n" ´                           # inclui o número da linha de correspondência
zsh%  find foo -name "* .p *" | xargs grep -E test
foo / bar.p0: 151: # data = teste
foo / bar.p1: 122: # data = teste # números de linha incluídos
zsh% unalias grep 
zsh% find foo -name "* .p *" | xargs grep -E test
foo / bar.p0: # data = teste
foo / bar.p1: # data = teste # números de linha não incluídos
zsh% 

1

Uma solução mais simples e elegante é usar a substituição de processo :

grep -E 'regex_here' <( find . -name '*.py')

Ele não cria um novo shell como o pipe, o que significa que você ainda está no shell original onde o alias está definido e a saída é exatamente o que você deseja que seja.

Apenas tome cuidado para não deixar espaço entre o redirecionamento e os parênteses, caso contrário o bash gerará um erro. Que eu saiba, a substituição do processo é suportada por Bash, Zsh, Ksh {88,93}, mas não por pdksh (disseram-me que ainda não deve ser ).


Eu acho que o desenvolvimento do pdksh está morto. Mksh é mais ou menos um projeto sucessor - “bem difícil, ao que parece (o conceito de análise é feito na cabeça de tg @)” .
— Gilles 'SO- stop be evil'

A substituição de processo é um bom método para findcomandos complexos , embora você precise tomar cuidado para que ele se quebre em espaços e que não possa ser corrigido da maneira mais fácil find | xargspossível (alternando para -print0e -0ou usando -exec). Quando aplicável, **/é mais simples e mais robusto.
— Gilles 'SO- stop be evil'

0

O grep lerá um conjunto de opções padrão da variável de ambiente GREP_OPTIONS. Se você vai colocar

 export GREP_OPTIONS='--line-number --color=always'

no seu .bashrc, a variável será transferida para os subshells e você obterá os resultados esperados.


Mas não colocar --line-numberou --color=alwaysem GREP_OPTIONSmenos que seja apenas para um comando, isso vai quebrar um monte de scripts. --color=autoé bom ter lá, e isso é tudo. Colocar essa linha no seu .bashrcvai quebrar muitas coisas.
— Gilles 'SO- stop be evil'

@Gilles Definir aliases ou substituir opções padrão para qualquer comando da conta raiz é uma coisa ruim. É improvável que a configuração dessas opções para uma conta de usuário cause muitos problemas. Não consigo criar scripts de usuário problemáticos.
— doneal24

Qualquer script que use grep de uma maneira que vá além do teste da presença de uma ocorrência será interrompido. Por exemplo, de /etc/init.d/cronno meu sistema: value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` . Ou a partir de /usr/bin/pdfjam: pdftitl=`printf "%s" "$PDFinfo" | grep -e … | sed -e …` . Um alias não é um problema, pois não é visto em scripts.
— Gilles 'SO- stop be evil'

@ Gilles Estou ciente de muitos scripts como este. As que eu não consigo entender são geralmente executadas apenas pelo root (como /etc/init.d/cron). Pessoalmente, não tenho nenhum alias definido em minhas contas de usuário, nem defino opções em um arquivo rc ou através de variáveis ​​de ambiente para substituir o comportamento padrão dos comandos. Prefiro previsibilidade do que conveniência.
— doneal24

Os aliases não quebram a previsibilidade, pois não são vistos pelos scripts. Definir GREP_OPTIONSmuito a previsibilidade das quebras, exceto por algumas opções --color=auto( como foi para isso que foi projetado).
— Gilles 'SO- stop be evil'
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.