Scripting di shell UNIX: come spostare ricorsivamente i file in una directory?


8

Ho un gran numero di piccoli file, f, disposti in una struttura di directory in questo modo:

/A/B/C/f

Esistono 11 directory al livello di A, ognuna con circa 100 directory al livello di B, ognuna con circa 30 directory al livello di C, ognuna con un file f.

Come faccio a spostare tutti i file di un livello? Ad esempio, dato questo set di file ...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

Voglio che la directory /A/B/contenga 4 file, da f1 a f4. La rimozione delle directory C non è necessaria.

Spero che questo è un problema risolto, possibilmente coinvolgendo find, xargse whatnot. Qualche idea?

Saluti,

Giacomo

Risposte:


9

È abbastanza semplice con GNU find (come si trova su Linux) o qualsiasi altra scoperta che supporti -execdir:

find A -type f -execdir mv -i {} .. \;

Con uno standard find:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

Con zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

Se la struttura della directory ha sempre lo stesso livello di annidamento, non è necessario alcun attraversamento ricorsivo (ma rimuovere prima le directory vuote):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

Per quel set di file, ciò farebbe:

$ cd /A/B/C/
$ mv ./* ../

Ma mi aspetto che il tuo problema sia un po 'più complicato ... Non posso rispondere a questo ... Non sono sicuro di come sia la tua struttura dir ... Potresti chiarire?


Questo ovviamente funzionerà per qualsiasi directory / A / B / C, ma dato che ho circa 30000 di queste directory, spero in un comando che possa essere eseguito nella parte superiore della gerarchia per occuparsene tutte in una volta.
jl6,

1
@ jl6, vedo, e solo i file devono essere spostati? Quindi C non deve spostarsi su B e B non su A, ecc ...
Stack Overflow è morto

È corretto: l'operazione deve essere eseguita solo sui file, al livello più basso della gerarchia, e dovrebbero essere spostati solo di un livello.
jl6,

0

La mia prima ipotesi è

$ find A -type f -exec mv {} .. \;  

fintanto che non specifichi -depthche dovrebbe essere ok. NON l'ho provato, quindi provalo prima di impegnarti.


Cosa trova fare quando ci sono due file con lo stesso nome?

Trova non è il problema, lo è il comando "mv" incorporato. I nomi duplicati non sarebbero buoni poiché mv non rinominerà, sovrascriverà. Puoi usare mv -i per aiutare un po ', ma con oltre 30.000 file che potrebbero essere dolorosi. Se hai bisogno di avere così tanto controllo sulla situazione, potresti aver bisogno di un programma scritto in un linguaggio di scripting (la mia scelta sarebbe python, YMMV).
hotei,

Non ho usato "backup" con mv, ma guardando la pagina man sembra che potrebbe essere una soluzione al tuo problema di nomi duplicati. "trova A -type f -exec mv - backup numerato {} .. \;" potrebbe funzionare (nota che è un doppio trattino in --backup)
hotei

L'esempio find fornito sposta tutti i file al livello più basso in una directory sopra A (credo che il ".." venga interpretato dalla shell prima di essere passato a find / mv?). Ho bisogno che i file si spostino di un solo livello nella gerarchia. Un ulteriore chiarimento: non ci saranno nomi di file duplicati, purché i file passino di UN solo livello.
jl6,

@hotei: i mvcomandi vengono eseguiti nella directory corrente, quindi ..non fare ciò che speri. Vedi la mia risposta per soluzioni. @ jl6: la shell non ha nulla a che fare con .., gestita dal kernel.
Gilles 'SO- smetti di essere malvagio' il

0

Se solo si desidera spostare i file che si trovano nelle directory foglia (cioè non si vuole spostare /A/B/filea /Ase Bcontiene le sottodirectory) allora qui ci sono un paio di modi per farlo:

Entrambi lo richiedono

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

Questo funziona:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

Questo sarà più veloce, ma non gli piacciono le directory dei sorgenti vuote:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
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.