Utilizzo dei dati letti da una pipe anziché da un file nelle opzioni di comando


18

Per definizione umana, questo comando ottiene l'input da un file.

$ command -r FILENAME

Supponiamo che FILENAMEsia un file contenente un elenco di nomi di file, come è stato generato usando ls > FILENAME.

Come posso invece alimentare direttamente il comando con il risultato di ls? Nella mia testa dovrebbe essere possibile qualcosa del genere:

$ ls | command -r

Ma non lo è, l'output di lsnon viene agganciato come argomento. Produzione:

Usage: command -r FILENAME
error: -r option requires an argument

Come posso ottenere l'effetto desiderato?


Altre risposte su questo in una domanda correlata: Come passare una stringa a un comando che prevede un file?
ilkkachu,

Risposte:


31

Questo dipende dal comando. Alcuni comandi che leggono da un file prevedono che il file sia un file normale, le cui dimensioni sono note in anticipo e che possono essere lette da qualsiasi posizione e riavvolte. Ciò è improbabile se il contenuto del file è un elenco di nomi di file: quindi il comando sarà probabilmente contenuto con una pipe che leggerà in sequenza dall'inizio alla fine. Esistono diversi modi per inviare dati tramite una pipe a un comando che prevede un nome file.

  • Molti comandi trattano -come un nome speciale, il che significa leggere dall'input standard anziché aprire un file. Questa è una convenzione, non un obbligo.

    ls | command -r -
  • Molte varianti di unix forniscono file speciali /devche designano i descrittori standard. Se /dev/stdinesiste, aprirlo e leggere da esso equivale a leggere dallo standard input; allo stesso modo /dev/fd/0se esiste.

    ls | command -r /dev/stdin
    ls | command -r /dev/fd/0
  • Se la tua shell è ksh, bash o zsh, puoi fare in modo che la shell gestisca l'attività di allocazione di un descrittore di file. Il vantaggio principale di questo metodo è che non è legato all'input standard, quindi puoi usare l'input standard per qualcos'altro e puoi usarlo più di una volta.

    command -r <(ls)
  • Se il comando prevede che il nome abbia una forma particolare (in genere una particolare estensione), puoi provare a ingannarlo con un collegamento simbolico.

    ln -s /dev/fd/0 list.foo
    ls | command -r list.foo

    Oppure puoi usare una pipe con nome.

    mkfifo list.foo
    ls >list.foo &
    command -r list.foo

Si noti che la generazione di un elenco di file lsè problematica perché lstende a manipolare i nomi dei file quando contengono caratteri non stampabili. printf '%s\n' *è più affidabile: stampa letteralmente ogni byte nei nomi dei file. I nomi dei file che contengono newline causeranno comunque problemi, ma ciò è inevitabile se il comando prevede un elenco di nomi di file separati da newline.


+1 per elencare tutte le possibilità. La sostituzione del processo IMO è la risposta corretta per questa situazione.
Glenn Jackman,

7

Dovrebbe essere:

ls | xargs -n 1 command -r

Modifica: per nomi con spazi vuoti:

ls | xargs -d '\n' -n 1 command -r

Questo potrebbe essere problematico se commandritiene che abbia tutti i nomi dei file necessari sulla riga di comando contemporaneamente. Alcune versioni di xargs daranno commandalcuni nomi di file (10, mi sembra) alla volta. Sembra che GNU xargs dia tutto in una volta.
Bruce Ediger,

2

In realtà, l'unica soluzione affidabile che conosco in grado di gestire tutti i nomi di file, compresi quelli con una nuova riga, è:

find . -maxdepth 1 -print0 | xargs -n 1 -0 command -r

L'unico carattere non consentito nel nome del file in questo caso è il carattere null, che non è comunque consentito nei nomi dei file.


1

Molti comandi accettano -come "nome file" che significa "usa l'input standard", ma questa convenzione è tutt'altro che universale. Leggi la pagina man.


1

Questo dovrebbe funzionare per il tuo scopo: ls | command -r /dev/stdin

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.