2014-07-27 6 views
8

Penso a xargs come la funzione di mappa della shell UNIX. Qual è la funzione filter?Se xargs è la mappa, che cos'è il filtro?

MODIFICA: sembra che dovrò essere un po 'più esplicito.

Diciamo che devo consegnare un programma che accetta una singola stringa come parametro e restituisce con un codice di uscita di 0 o 1. Questo programma fungerà da predicato sulle stringhe che accetta.

Ad esempio, potrei decidere di interpretare il parametro stringa come percorso file e definire il predicato come "questo file esiste". In questo caso, il programma potrebbe essere test -f, che, data una stringa, esce con 0 se il file esiste e 1 altrimenti.

Devo anche passare un flusso di stringhe. Ad esempio, potrei avere un file contenente ~/paths

/etc/apache2/apache2.conf 
/foo/bar/baz 
/etc/hosts 

Ora, voglio creare un nuovo file, ~/existing_paths, contenente solo i percorsi che esistono sul mio file system. Nel mio caso, che sarebbe

/etc/apache2/apache2.conf 
/etc/hosts 

voglio fare questo leggendo nel file ~/paths, filtrando quelle linee dal predicato test -f, e scrivere l'output a ~/existing_paths. Per analogia con xargs, questo sarà simile:

cat ~/paths | xfilter test -f > ~/existing_paths 

E 'il programma ipotizzato xfilter che sto cercando:

xfilter COMMAND [ARG]... 

Il che, per ogni linea L del suo standard input, chiamerà COMMAND [ARG]... L e se il codice di uscita è 0, stampa L, altrimenti non stampa nulla.

Per essere chiari, io non sono alla ricerca di:

  • un modo per filtrare un elenco di filepaths dall'esistenza. Questo è stato un esempio specifico.
  • come scrivere un programma del genere. Posso farlo.

ho am alla ricerca di uno:

  • un'implementazione preesistente, come xargs o
  • una chiara spiegazione del perché questo non esiste
+1

Perché non è efficace ** forcella ** Nt Imita un comando ciò che solo il filer baserà sullo stato di uscita. Come hai affermato, è semplice scrivere in qualsiasi lingua (bash, perl, C) - ma non è efficace. Molto più efficace sta usando direttamente un comando (lo strumento giusto - basato sulla situazione) che cosa potrebbe leggere _STDIN_ e _filter l'input_ come zilion volte ** fork/exec ** un comando per exit-status. Molte volte il 'xargs' non è il modo più efficace. (Immagina un elenco di file lungo 1_000_000 linee. Le forcelle Milion non sono la cosa migliore che puoi fare ...) E se hai bisogno di qualcosa del genere (come sai) è una funzione bash a 3 linee – jm666

+3

@ jm666 suona come " efficace "intendi" performante ". Non mi importa delle prestazioni, mi interessa dell'espressività. – jameshfisher

risposta

1

È può avere awk fare la funzione filter e reduce.

Filtro:

awk 'NR % 2 { $0 = $0 " [EVEN]" } 1' 

Ridurre:

awk '{ p = p + $0 } END { print p }' 
+1

Grazie, ma 'xargs' ha come parametro un comando di shell generale, che è la funzione per mappare sulle linee di input. Per analogia, 'filter' dovrebbe utilizzare un comando shell da utilizzare come predicato sulle linee di input (ad esempio, in base al codice di ritorno che è 0 o meno). Nei tuoi esempi, i predicati sono definiti solo in "awk'-speak. – jameshfisher

+0

@jameshfisher Quello che ti serve è la shell, immagino. – konsolebox

+0

Non sono sicuro di cosa intendi - come [questo] (http://stackoverflow.com/questions/255898/how-to-iterate-over-arguments-in-bash-script)? – jameshfisher

3

Quindi, siete alla ricerca per il:

reduce( compare( filter(map(.. list())))) 

ciò che può essere rewiritten come

list | map | filter | compare | reduce 

L'alimentazione principale di bash è un pipelining, pertanto non è necessario disporre di un comando speciale filter e/o reduce. Infatti quasi tutti unix comandi potrebbero agire in uno (o più) funzioni come:

  • lista
  • mappa
  • filtro
  • ridurre

Imagine:

find mydir -type f -print | xargs grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head -1 
^------list+filter------^ ^--------map-----------^ ^--filter--^ ^compare^ ^reduce^ 

Creazione di un test case:

mkdir ./testcase 
cd ./testcase || exit 1 
for i in {1..10} 
do 
    strings -1 < /dev/random | head -1000 > file.$i.txt 
done 
mkdir emptydir 

otterrete una directory chiamata testcase e in questa directory 10 file e una directory

emptydir file.1.txt file.10.txt file.2.txt file.3.txt file.4.txt file.5.txt file.6.txt file.7.txt file.8.txt file.9.txt 

ogni file contiene 1000 righe di stringhe casuali alcune linee sono contiene solo numeri

ora eseguire il comando

find testcase -type f -print | xargs grep -H '^[0-9]*$' | cut -d: -f 2 | sort -nr | head -1 

e si otterrà la più grande linea numero solo da ogni file come: 42. (Naturalmente, questo può essere fatto in modo più efficace, questo è solo per demo)

decomposto:

Il find testcase -type f -print stamperà ogni file semplici così, LIST (e ridotti solo ai file). ouput:

testcase/file.1.txt 
testcase/file.10.txt 
testcase/file.2.txt 
testcase/file.3.txt 
testcase/file.4.txt 
testcase/file.5.txt 
testcase/file.6.txt 
testcase/file.7.txt 
testcase/file.8.txt 
testcase/file.9.txt 

la xargs grep -H '^[0-9]*$' come MAPPA verrà eseguito un comando grep per ogni file da un elenco. Il grep usa solitamente come filtro , ad esempio: command | grep, ma ora (con xargs) cambia l'input (nomi di file) in (righe contenenti solo cifre). Uscita, molte linee piace:

testcase/file.1.txt:1 
testcase/file.1.txt:8 
.... 
testcase/file.9.txt:4 
testcase/file.9.txt:5 

struttura delle linee: filename colon number, vogliono solo i numeri in modo che richiedono un filtro puro, quello strisce fuori i nomi dei file da ogni riga cut -d: -f2.E 'uscite molte linee come:

1 
8 
... 
4 
5 

Ora la riducono (ricevendo il maggior numero), i sort -nr genere tutti i numeri numerico e l'ordine (decrescente) inversa, quindi la sua uscita è come:

42 
18 
9 
9 
... 
0 
0 

e il head -1 stampa la prima riga (il numero più grande).

Naturalmente, è possibile scrivere il proprio elenco/filtri/mappa/ridurre le funzioni direttamente con bash costruzioni di programmazione (loop, le condizioni e simili), oppure è possibile utilizzare qualsiasi linguaggio di scripting fullblown come perl, lingue speciali come awk, sed "lingua", o dc (rpn) e così via.

Avere un comando speciale filtro quali:

list | filter_command cut -d: -f 2 

è semplice non necessaria, perché è possibile utilizzare direttamente il

list | cut 
4

Se mappa è xargs, filtro è ... ancora xargs.

Esempio: file di elenco nella directory corrente e filtrare i file non eseguibili:

ls | xargs -I{} sh -c "test -x '{}' && echo '{}'" 

Questo potrebbe essere fatto a portata di mano attraverso una funzione (non pronto per la produzione):

xfilter() { 
    xargs -I{} sh -c "$* '{}' && echo '{}'" 
} 
ls | xfilter test -x 

alternativa , è possibile utilizzare un'implementazione di filtri paralleli tramite GNU Parallel:

ls | parallel "test -x '{}' && echo '{}'"