Pracuję nad skryptem, który musi wykonać akcję w każdym podkatalogu określonego folderu.
Jaki jest najskuteczniejszy sposób na napisanie tego?
bash
command
directory-traversal
mikewilliamson
źródło
źródło
mkdir 'foo * bar'
, spowodujefoo
ibar
zostanie iterowane, nawet jeśli nie istnieją i*
zostaną zastąpione listą wszystkich nazw plików, nawet tych, które nie należą do katalogu).mkdir -p '/tmp/ /etc/passwd /'
- jeśli ktoś uruchomi skrypt zgodnie z tą praktyką,/tmp
aby na przykład znaleźć katalogi do usunięcia, może skończyć się usunięciem/etc/passwd
.Odpowiedzi:
źródło
find
jeśli chcesz zachować rekurencyjne lub nierekurencyjne zachowanie.find . -mindepth 1 -type d
mkdir 'directory name with spaces'
na cztery osobne słowa.-mindepth 1 -maxdepth 1
bo poszło zbyt głęboko.Wersja, która unika tworzenia podprocesu:
Lub, jeśli twoje działanie jest pojedynczym poleceniem, jest to bardziej zwięzłe:
Lub jeszcze bardziej zwięzła wersja ( dzięki @enzotib ). Zauważ, że w tej wersji każda wartość
D
będzie miała ukośnik końcowy:źródło
if
lub za[
pomocą:for D in */; do
for D in *; do [ -d "${D}" ] && my_command; done
lub kombinacji dwóch ostatnich:for D in */; do [ -d $D ] && my_command; done
for D in .* *; do
zamiast tegofor D in *; do
.Najprostszym nierekurencyjnym sposobem jest:
Na
/
końcu jest napisane, że należy używać tylko katalogów.Nie ma takiej potrzeby
źródło
shopt -s dotglob
podczas rozwijania symboli wieloznacznych można używać plików / plików dotdir. Zobacz także: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html/*
, zamiast*/
z/
reprezentującą ścieżkę, której chcesz użyć./*
byłoby absolutną ścieżką, podczas gdy*/
zawierałoby podkatalogi z bieżącej lokalizacji${d%/*}
Użyj
find
polecenia.W GNU
find
możesz użyć-execdir
parametru:lub za pomocą
-exec
parametru:lub z
xargs
poleceniem:Lub używając pętli for :
W przypadku rekurencyjności spróbuj
**/
zamiast tego rozszerzyć globbing ( ) (włącz: :)shopt -s extglob
.Aby uzyskać więcej przykładów, zobacz: Jak przejść do każdego katalogu i wykonać polecenie? w SO
źródło
-exec {} +
jest określony przez POSIX,-exec sh -c 'owd=$PWD; for arg; do cd -- "$arg" && pwd -P; cd -- "$owd"; done' _ {} +
jest kolejną legalną opcją i wywołuje mniej powłok niż-exec sh -c '...' {} \;
.Poręczne jednowarstwowe
Opcja A jest poprawna dla folderów ze spacjami pomiędzy nimi. Ponadto, ogólnie szybciej, ponieważ nie drukuje każdego słowa w nazwie folderu jako osobnej jednostki.
źródło
find . -type d -print0 | xargs -0 -n 1 my_command
źródło
my_command
.Spowoduje to utworzenie podpowłoki (co oznacza, że wartości zmiennych zostaną utracone po zamknięciu
while
pętli):To nie będzie:
Oba będą działać, jeśli w nazwach katalogów są spacje.
źródło
find ... -print0
iwhile IFS="" read -r -d $'\000' dir
-d ''
jest to mniej mylące w kwestii składni bash i możliwości, ponieważ-d $'\000'
sugeruje (fałszywie), że$'\000'
w pewien sposób różni się od''
- w rzeczywistości można łatwo (i znowu fałszywie) wywnioskować z to, że bash obsługuje łańcuchy w stylu Pascala (określone długości, mogą zawierać literały NUL), a nie łańcuchy C (rozdzielone NUL, nie mogą zawierać NUL).Możesz spróbować:
lub podobne...
Wyjaśnienie:
find . -maxdepth 1 -mindepth 1 -type d
: Znajdź tylko katalogi o maksymalnej rekurencyjnej głębokości 1 (tylko podkatalogi 1 USD) i minimalnej głębokości 1 (wyklucza bieżący folder.
)źródło
cd "$f"
. Nie działa, gdy dane wyjściowefind
są podzielone na ciągi, więc będziesz mieć oddzielne fragmenty nazwy jako osobne wartości$f
, co sprawi, że dobrze sobie poradzisz , lub nie będziesz cytował$f
rozwinięcia.find
Dane wyjściowe są zorientowane liniowo (teoretycznie jedna nazwa na linię - ale patrz poniżej) z domyślną-print
akcją.find -print
nie jest bezpiecznym sposobem na przekazywanie dowolnych nazw plików, ponieważ można uruchomić cośmkdir -p $'foo\n/etc/passwd\nbar'
i uzyskać katalog, który ma/etc/passwd
osobną linię w nazwie. Obsługa nazw z plików w katalogach/upload
lub/tmp
katalogów bez opieki to świetny sposób na ataki polegające na eskalacji uprawnień.zaakceptowana odpowiedź złamie się na białych spacjach, jeśli zawierają je nazwy katalogów, a preferowaną składnią jest
$()
bash / ksh. UżyjGNU find
-exec
opcji z+;
npfind .... -exec mycommand +;
#this is same as passing to xargs
lub użyj pętli while
źródło
find -exec
przejściem a przejściem doxargs
:find
zignoruje wartość wyjściową wykonywanego polecenia, axargs
zakończy się niepowodzeniem przy niezerowym wyjściu. Każda z nich może być poprawna, w zależności od potrzeb.find ... -print0 | while IFS= read -r d
jest bezpieczniejszy - obsługuje nazwy rozpoczynające się lub kończące spacją oraz nazwy zawierające literały nowego wiersza.