Limitare POSIX a una profondità specifica?


15

Ho notato di recente che le specifiche POSIX perfind non includono il -maxdepthprimario.

Per coloro che non hanno familiarità con esso, lo scopo del -maxdepthprimario è limitare il numero di livelli findche scenderanno in profondità . -maxdepth 0comporta l' elaborazione solo degli argomenti della riga di comando; -maxdepth 1gestirà i risultati solo direttamente all'interno degli argomenti della riga di comando, ecc.

Come posso ottenere il comportamento equivalente al -maxdepthprimario non POSIX utilizzando solo opzioni e strumenti specificati da POSIX?

(Nota: ovviamente posso ottenere l'equivalente -maxdepth 0semplicemente usando -prunecome primo operando, ma ciò non si estende ad altre profondità.)


L'approccio di @StevenPenny, FreeBSD -depth -2, -depth 1... potrebbe essere visto come migliore di GNU -maxdepth/-mindepth
Stéphane Chazelas il

@ StéphaneChazelas in entrambi i modi - POSIX find dovrebbe avere l'uno o l'altro; altrimenti è paralizzato
Steven Penny,

1
Almeno per -maxdepth/ -mindepth, ci sono alternative ragionevoli (si noti che -pathè una recente aggiunta a POSIX). Le alternative per -timexyo -mtime -3m(o -mmin -3) sono molto più ingombranti. Alcuni come -execdir/ -deletenon hanno alternative affidabili.
Stéphane Chazelas,

2
@StevenPenny, sentiti libero di registrare un biglietto su austingroupbugs.net per richiederne l'aggiunta. Ho visto che le cose venivano aggiunte senza la necessità di uno sponsor quando c'era una forte giustificazione. Un modo di agire probabilmente migliore sarebbe ottenere quante più implementazioni lo aggiungono prima, quindi POSIX dovrebbe solo specificare ciò che è generalmente meno controverso.
Stéphane Chazelas,

@ StéphaneChazelas nel mio caso ho finito col nominare direttamente i file, ma grazie; Potrei presentare un biglietto se questo dovesse ripresentarsi
Steven Penny,

Risposte:


7

È possibile utilizzare -pathper abbinare una data profondità e potare lì. Per esempio

find . -path '*/*/*' -prune -o -type d -print

sarebbe maxdepth 1, come le *partite ., le */*partite ./dir1e le */*/*partite ./dir1/dir2che sono potate. Se si utilizza una directory di partenza assoluto è necessario aggiungere una porta /al -pathtroppo.


Hmmm, difficile. Non potresti semplicemente rimuovere uno strato di /*dalla fine del modello, eliminare l' -ooperatore e ottenere lo stesso risultato?
Wildcard l'

No, perché anche le *partite /, quindi il dir a/b/c/d/esi adatterebbe -path */*, purtroppo.
Meuh

Ma a/b/c/d/enon verrebbe mai raggiunto , perché -pruneverrebbe applicato a a/b....
Wildcard

1
Mi dispiace, l'ho letto male -prunee -osono stato rimosso. Se -pruneil problema */*persiste è che non corrisponderà a nulla a un livello superiore a maxdepth, ad esempio la singola directory a.
Meuh

11

L'approccio di @ meuh è inefficiente in quanto il suo -maxdepth 1approccio consente ancora di findleggere il contenuto delle directory al livello 1 per ignorarle successivamente. Inoltre non funzionerà correttamente con alcune findimplementazioni (incluso GNU find) se alcuni nomi di directory contengono sequenze di byte che non formano caratteri validi nelle impostazioni locali dell'utente (come per i nomi di file con una codifica di caratteri diversa).

find . \( -name . -o -prune \) -extra-conditions-and-actions

è il modo più canonico di implementare GNU -maxdepth 1(o FreeBSD -depth -2).

In genere, però, è -depth 1tu che vuoi ( -mindepth 1 -maxdepth 1) come non vuoi considerare .(profondità 0), e poi è ancora più semplice:

find . ! -name . -prune -extra-conditions-and-actions

Perché -maxdepth 2diventa:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Ed è qui che corri nei problemi con i personaggi non validi.

Ad esempio, se hai una directory chiamata Stéphanema écodificata nel set di caratteri iso8859-1 (aka latin1) (0xe9 byte) come era più comune in Europa occidentale e in America fino alla metà degli anni 2000, allora quel byte 0xe9 non è un carattere valido in UTF-8. Quindi, nelle versioni locali UTF-8, il *carattere jolly (con alcune findimplementazioni) non corrisponderà Stéphanepoiché *è 0 o più caratteri e 0xe9 non è un carattere.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

Il mio find(quando l'uscita va su un terminale) visualizza quel byte 0xe9 non valido come ?sopra. Potete vedere che St<0xe9>phane/Chazelasnon era pruned.

Puoi aggirare il problema facendo:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Tuttavia, ciò influisce su tutte le impostazioni locali finde su qualsiasi applicazione in esecuzione (come tramite i -execpredicati).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Ora, ho davvero un -maxdepth 2ma nota come l'é nel secondo Stéphane correttamente codificato in UTF-8 viene visualizzato come ??byte 0xc3 0xa9 (considerati come due singoli caratteri indefiniti nella locale C) della codifica UTF-8 di é caratteri non stampabili nella locale C.

E se avessi aggiunto un -name '????????', avrei ottenuto Stéphane sbagliato (quello codificato in iso8859-1).

Per applicare a percorsi arbitrari invece di ., devi fare:

find some/dir/. ! -name . -prune ...

per -mindepth 1 -maxdepth 1o:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

per -maxdepth 2.

Vorrei ancora fare un:

(cd -P -- "$dir" && find . ...)

In primo luogo perché ciò rende i percorsi più brevi, il che rende meno probabile che si verifichino percorsi troppo lunghi o che elenchi problemi troppo lunghi , ma anche per aggirare il fatto che findnon può supportare argomenti di percorso arbitrari (tranne -fcon FreeBSD find) in quanto si soffocano valori di $dirlike !o -print...


In -ocombinazione con la negazione è un trucco comune per eseguire due serie indipendenti di -condition/ -actionin find.

Se si desidera eseguire -action1su riunioni di file -condition1e indipendentemente -action2su riunioni di file -condition2, non è possibile eseguire:

find . -condition1 -action1 -condition2 -action2

Come -action2verrebbe eseguito solo per i file che soddisfano entrambe le condizioni.

Né:

find . -contition1 -action1 -o -condition2 -action2

Come -action2non verrebbe eseguito per i file che soddisfano entrambe le condizioni.

find . \( ! -condition1 -o -action1 \) -condition2 -action2

funziona come \( ! -condition1 -o -action1 \)risolverebbe vero per ogni file. Questo presuppone -action1è un'azione (come -prune, -exec ... {} +) che restituisce sempre vero . Per azioni del genere -exec ... \;può sembrare falso , potresti voler aggiungere un altro -o -somethingdove -somethingè innocuo ma restituisce vero come -truein GNU findo -links +0o -name '*'(anche se nota il problema relativo ai caratteri non validi sopra).


1
Un giorno mi imbatterò in un mucchio di file cinesi e sarò molto contento di aver letto le tue molte risposte su impostazioni internazionali e caratteri validi. :)
Wildcard il

2
@Wildcard, tu (e ancora di più una persona cinese) è più probabile che si verifichino problemi con nomi di file inglesi, francesi ... rispetto ai nomi di file cinesi poiché i nomi di file cinesi sono più spesso codificati in UTF-8 rispetto ai nomi di file di script alfabetici che generalmente può essere coperto da un set di caratteri a byte singolo che era la norma fino a tempi relativamente recenti. Ci sono altri set di caratteri multi-byte per coprire il carattere cinese, ma mi aspetto che i cinesi sarebbero passati a UTF-8 prima degli occidentali poiché quei set di caratteri hanno una serie di problemi cattivi. Vedi anche la modifica per un esempio.
Stéphane Chazelas,

0

Ho riscontrato un problema in cui avevo bisogno di un modo per limitare la profondità durante la ricerca di più percorsi (anziché solo .).

Per esempio:

$ find dir1 dir2 -name myfile -maxdepth 1

Questo mi ha portato ad un approccio alternativo usando -regex. L'essenza è:

-regex '(<list of paths | delimited>)/<filename>'

Quindi, quanto sopra sarebbe:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

Senza un nome file:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

Infine, per -maxdepth 2la regex cambia in:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'


1
Questa domanda richiede una soluzione standard (come in POSIX). Funzionerebbe anche -maxdepthcon più percorsi di ricerca.
Kusalananda
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.