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.