2013-03-26 16 views
27

Voglio echo la parte del nome file di un find sulla riga di comando di Linux. Ho cercato di utilizzare il seguente:Trova e basename non stanno giocando bene

find www/*.html -type f -exec sh -c "echo $(basename {})" \; 

e

find www/*.html -type f -exec sh -c "echo `basename {}`" \; 

e tutta una serie di altre combinazioni di fuga e citando varie parti del testo. Il risultato è che il percorso non è stato rimosso:

www/channel.html 
www/definition.html 
www/empty.html 
www/index.html 
www/privacypolicy.html 

Perché no?

Aggiornamento: Mentre ho una soluzione di lavoro di seguito, sono ancora interessato al motivo per cui "basename" non fa quello che dovrebbe fare.

risposta

40

Il problema con il tentativo originale:

find www/*.html -type f -exec sh -c "echo $(basename {})" \; 

è che il codice $(basename {}) viene eseguito una volta, prima dell'esecuzione del comando find buito. L'output del singolo basename è {} poiché quello è il nome di base di {} come nome file. Così, il comando che viene eseguito dal ritrovamento è:

sh -c "echo {}" 

per ogni file trovato, ma find sostituisce in realtà il nome del file (non modificato) originale ogni volta perché i {} personaggi appaiono nella stringa da eseguire.

Se si voleva farlo funzionare, è possibile utilizzare le virgolette singole al posto delle virgolette:

find www/*.html -type f -exec sh -c 'echo $(basename {})' \; 

Tuttavia, rendendo echo ripetere sullo standard output quello basename avrei scritto sullo standard output in ogni caso è un po 'inutile:

find www/*.html -type f -exec sh -c 'basename {}' \; 

e siamo in grado di ridurre ulteriormente che, naturalmente, a:

find www/*.html -type f -exec basename {} \; 

Potresti anche spiegare la differenza tra virgolette singole e virgolette doppie qui?

Questo è un comportamento di shell di routine. Prendiamo un comando leggermente diverso (ma solo leggermente - i nomi dei file potrebbero essere ovunque sotto la directory www, non solo di un livello inferiore), e guardiamo le versioni a virgolette singole (SQ) e doppie quote (DQ) di il comando:

find www -name '*.html' -type f -exec sh -c "echo $(basename {})" \; # DQ 
find www -name '*.html' -type f -exec sh -c 'echo $(basename {})' \; # SQ 

Le virgolette singole passano il materiale allegato direttamente al comando.Così, nella riga di comando SQ, la shell che lancia find rimuove le virgolette che racchiudono e il comando find vede il suo argomento $9 come:

echo $(basename {}) 

perché la shell rimuove le virgolette. In confronto, il materiale tra virgolette viene elaborato dalla shell. Così, nella riga di comando DQ, il guscio (che lancia find - non quella lanciata dafind) vede la parte $(basename {}) della stringa e lo esegue, tornando {}, quindi la stringa si passa alla find come argomento $9 è:

echo {} 

Ora, quando fa il suo find-exec azione, in entrambi i casi sostituisce il {} dal nome del file che appena trovato (per amor di discussione, www/pics/index.html). Così, si ottiene due comandi diversi in corso di esecuzione:

sh -c 'echo $(basename www/pics/index.html)' # SQ 
sh -c "echo www/pics/index.html"    # DQ 

C'è una (lieve) imbroglio notazione succedendo lì - quelli sono i comandi equivalenti che devi digitare in corrispondenza dello scafo. Lo $2 della shell che viene lanciata in realtà non contiene virgolette in entrambi i casi: la shell lanciata non vede alcuna virgoletta.

Come si può vedere, il comando DQ fa semplicemente eco al nome del file; il comando SQ esegue il comando basename e ne cattura l'output, quindi esegue l'eco dell'output acquisito. Un po 'di pensiero riduzionista mostra che il comando DQ potrebbe essere scritto come -print invece di usare -exec e il comando SQ potrebbe essere scritto come -exec basename {} \;.

Se stai usando GNU find, supporta l'azione -printf che può essere seguita da Format Directives tale che l'esecuzione basename non è necessaria. Tuttavia, questo è disponibile solo in GNU find; il resto della discussione qui si applica a qualsiasi versione di find che è probabile incontrare.

+0

Questa è un'ottima risposta e spiegazione. Potresti anche spiegare la differenza tra virgolette singole e doppie virgolette qui? –

+1

Grazie mille per aver aggiunto le informazioni sulle citazioni –

+0

Non c'è ovviamente alcun motivo per usare 'sh -c' qui, ma' -exec sh -c 'echo "$ (basename" {} ")"' \; ', dovrebbe ancora includere virgolette per impedire la suddivisione in parole. in tal caso, '-exec sh -c 'echo" $ (basename "$ 1") "' sh {} \;' sarebbe davvero preferibile. – BroSlow

10

Prova a modificare:

find www/*.html -type f -printf '%f\n' 

Se si vuole fare con un tubo (più risorse necessarie):

find www/*.html -type f -print0 | xargs -0 -n1 basename 
+0

Grazie, ma ottenere lo stesso risultato :( –

+0

messaggio modificato di conseguenza Tty nuovo –

+0

Ok, che funziona, grazie Qualsiasi idea di ciò che è sbagliato con il nome base –

6

Thats come ho ridimensionamento batch file con imagick, rediving il nome del file in uscita dalla sorgente

find . -name header.png -exec sh -c 'convert -geometry 600 {} $(dirname {})/$(basename {} ".png")_mail.png' \; 
+0

Snippet molto utile, grazie –

0

ho dovuto realizzare qualcosa di simile, e hanno trovato seguendo le tecniche di cui per evitare loop oltre l'output di find e l'utilizzo di trovare con sh eluso questi problemi con {} e -printf interamente.

Si può provare in questo modo:

find www/*.html -type f -exec sh -c 'echo $(basename $1)' find-sh {} \; 

La sintesi è "Non fare riferimento {} direttamente all'interno di un -c sh ma invece passarlo a sh -c come argomento, allora si può riferimento con una variabile numero all'interno di sh -c "il find-sh è appena lì come un manichino per occupare il $0, c'è più utilità nel farlo in quel modo e utilizzando {} per $1.

Suppongo che l'utilizzo di echo semplifichi davvero il concetto e la funzione di test.Ci sono modi più semplici per echeggiare semplicemente come altri hanno menzionato, ma un caso d'uso ideale per questo scenario potrebbe essere l'utilizzo di cp, mv, o di comandi più complessi in cui si desidera fare riferimento ai nomi di file trovati più di una volta nel comando ed è necessario per sbarazzarsi del percorso, ad es. quando devi specificare il nome del file sia nell'origine che nella destinazione o se stai rinominando le cose.

Così, per esempio, se si voleva copiare solo i documenti HTML al tuo public_html directory (Perché perché Esempio?!), Allora si potrebbe:

find www/*.html -type f -exec sh -c 'cp /var/www/$(basename $1) /home/me/public_html/$(basename $1)' find-sh {} \; 

Oltre unix StackExchange, la risposta di jolly utente su il looping con find entra in grandi gemme sull'uso di -exec e sh -c. (Lo potete trovare qui: https://unix.stackexchange.com/questions/321697/why-is-looping-over-finds-output-bad-practice).

Problemi correlati