Chiedi a xargs di utilizzare l'alias anziché binario


13

Bash 4.2 su CentOS 6.5:

Nel mio ~/.bash_profileho un sacco di alias, tra cui:

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

in modo da poter evidenziare il colore e stampare automaticamente i numeri di riga durante l'esecuzione grep. Se eseguo quanto segue, l'evidenziazione funziona come previsto:

$ grep -Re 'regex_here' *.py

Tuttavia, quando l'ho eseguito di recente:

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

i risultati non sono stati evidenziati e i numeri di riga non sono stati stampati, costringendomi a tornare indietro e aggiungere esplicitamente -n --color=alwaysal grepcomando.

  • Non xargslegge gli alias nell'ambiente?
  • In caso contrario, c'è un modo per farlo?

Questa domanda e risposta ha quello che vuoi.
psimon

@psimon giusto, in sostanza sta dicendo di fare quello che ho già fatto nella mia soluzione alternativa - ho dovuto espandere manualmente il mio alias nel xargscomando. Quello che sto cercando di scoprire è se c'è un modo da cui posso chiamare direttamente il mio alias xargs.
MattDMo

1
Hai provato export GREP_OPTIONS='-n --color=always'prima del tuo comando xargs?
doneal24,

@ DougO'Neal grazie, ha funzionato! Lo aggiungerò al mio .bash_profile. Sentiti libero di scrivere una risposta ...
MattDMo

Risposte:


10

Un alias è interno alla shell in cui è definito. Non è visibile ad altri processi. Lo stesso vale per le funzioni della shell. xargsè un'applicazione separata, che non è una shell, quindi non ha un concetto di alias o funzioni.

Puoi fare in modo che xargs invochi una shell invece di invocare grepdirettamente. Tuttavia, solo invocare una shell non è sufficiente, è necessario definire anche l'alias in quella shell. Se l'alias è definito nel tuo .bashrc, puoi sorgente quel file; tuttavia, ciò potrebbe non funzionare se si .bashrceseguono altre attività che non hanno senso in una shell non interattiva.

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

Fai attenzione alle complessità delle quotazioni nidificate quando digiti regexp. Puoi semplificare la tua vita passando regexp come parametro alla shell.

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

È possibile eseguire la ricerca alias in modo esplicito. Quindi xargsvedrà grep -n --color=always.

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

In zsh:

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

A proposito, nota che si find … | xargs … rompe su nomi di file contenenti spazi (tra gli altri) . È possibile risolvere questo problema passando a record delimitati da null:

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

o usando -exec:

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

Invece di chiamare find, puoi fare tutto interamente all'interno della shell. Il modello glob **/attraversa ricorsivamente le directory. In bash, devi eseguire shopt -s globstarper abilitare prima questo schema glob.

grep regex_here **/*.py

Questo ha alcune limitazioni:

  • Se molti file corrispondono (o se hanno percorsi lunghi), il comando potrebbe non riuscire perché supera la lunghezza massima della riga di comando.
  • In bash ≤4.2 (ma non nelle versioni più recenti, né in ksh o zsh), **/ricorre in collegamenti simbolici alle directory.

Un altro approccio consiste nell'utilizzare la sostituzione del processo, come suggerito da MariusMatutiae .

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

Ciò è utile quando **/non è applicabile: per findespressioni complesse o in bash ≤4.2 quando non si desidera ricorrere sotto collegamenti simbolici. Si noti che ciò si interrompe sui nomi dei file contenenti spazi; una soluzione è quella di serie IFSe globbing disabilitare , ma sta cominciando a diventare un po 'complessa:

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

grazie per la chiara spiegazione del perché gli alias non sono visibili ad altri processi
MattDMo

Si può anche usare la sostituzione di processo, vedere la mia risposta.
MariusMatutiae,

11

Uso 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.

Grazie per quello, non sapevo del trucco dello spazio finale.
MattDMo

NP. È utile anche con sudo...
1.61803,

2

Si prega di prendere questo come una dimostrazione di un altro approccio, che non riesco a trovare nella relativa domanda SO :

È possibile scrivere una funzione wrapper per la xargsquale verifica se il primo argomento è un alias e, in tal caso, espanderlo di conseguenza.

Ecco un codice che fa esattamente questo, ma purtroppo richiede la shell Z e quindi non esegue 1: 1 con bash (e, francamente, non sono abituato a bash abbastanza per portarlo):

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, che funziona:

zsh% alias grep = "grep -n" ´                           # include il numero di riga della corrispondenza
zsh% trova foo -name "* .p *" | xargs grep -E test
foo / bar.p0: 151: # Dati = test
foo / bar.p1: 122: # data = test # numeri di riga inclusi
zsh% unalias grep 
zsh% find foo -name "* .p *" | xargs grep -E test
foo / bar.p0: # Dati = test
foo / bar.p1: # data = test # numeri di riga non inclusi
% zsh 

1

Una soluzione più semplice ed elegante è utilizzare la sostituzione del processo :

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

Non crea una nuova shell come fa la pipe, il che significa che sei ancora nella shell originale in cui è definito l'alias e l'output è esattamente quello che desideri che sia.

Fai solo attenzione a non lasciare spazio tra reindirizzamento e parentesi, altrimenti bash genererà un errore. Per quanto ne so, la sostituzione del processo è supportata da Bash, Zsh, Ksh {88,93}, ma non da pdksh (mi è stato detto che dovrebbe essere un non ancora ).


Penso che lo sviluppo di pdksh sia morto. Mksh è più o meno un progetto successivo - "piuttosto difficile, si scopre (l'analisi del concetto è fatta nella testa di tg @)" .
Gilles 'SO- smetti di essere malvagio' il

La sostituzione del processo è un buon metodo per findcomandi complessi , anche se è necessario fare attenzione che si romperà sugli spazi e che non può essere risolto con la stessa facilità find | xargs(passando a -print0e -0, o usando -exec). Se applicabile, **/è più semplice e più robusto.
Gilles 'SO- smetti di essere malvagio' il

0

grep leggerà una serie di opzioni predefinite dalla variabile d'ambiente GREP_OPTIONS. Se lo metterai

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

nel tuo .bashrc la variabile verrà passata ai subshells e otterrai i risultati che ti aspetti.


Ma non inserire --line-numbero inserire --color=alwaysa GREP_OPTIONSmeno che non sia solo per un comando, questo spezzerà molti script. --color=autoè ok avere lì, e questo è tutto. Inserendo questa linea nel tuo .bashrcsi romperanno molte cose.
Gilles 'SO- smetti di essere malvagio' il

@Gilles Impostare qualsiasi alias o sovrascrivere le opzioni predefinite per qualsiasi comando per l'account root è una cosa negativa. L'impostazione di queste opzioni per un account utente difficilmente può causare molti problemi. Non riesco a trovare alcun script utente che sia problematico.
doneal24,

Quasi ogni script che utilizza grep in un modo che va oltre il test della presenza di un evento si interromperà. Ad esempio, dal /etc/init.d/cronsul mio sistema: value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` . O da /usr/bin/pdfjam: pdftitl=`printf "%s" "$PDFinfo" | grep -e … | sed -e …` . Un alias non è un problema poiché non è visibile negli script.
Gilles 'SO- smetti di essere malvagio' il

@Gilles Sono a conoscenza di molti script come questo. Quelli che non riesco ad aggirare sono generalmente gestiti solo da root (come /etc/init.d/cron). Personalmente, non ho alcun alias definito nei miei account utente né imposto opzioni in un file rc o tramite variabili di ambiente per sovrascrivere il comportamento predefinito dei comandi. Preferisco la prevedibilità piuttosto che la convenienza.
doneal24,

Gli alias non infrangono la prevedibilità poiché non sono visti dagli script. L'impostazione della GREP_OPTIONSprevedibilità delle interruzioni è molto negativa, ad eccezione di alcune opzioni come --color=auto(che è stata progettata per).
Gilles 'SO- smetti di essere malvagio' il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.