Shell scripting: seleziona la cartella in base a una parte del nome del file

3

Il mio progetto

Sto creando uno script di shell bash da eseguire dal terminale. Il suo scopo è archiviare molte e molte cartelle di progetto. Ogni cartella segue una nomenclatura prescritta: [YYYY.MM.DD] - Medium - Client - Project name - details--details - JobNumber . Ad esempio: [2006.02.01] - Print - Development - Appeal I - Kids Art Show Insert - D0601-11 . Questi progetti sono attualmente una cartella. Voglio ordinarli in cartelle in base al nome del cliente. Ci sono 7 client (interni), quindi sto usando il seguente script di shell:

#!/bin/bash

# Go to the Completed Projects folder.
cd /Volumes/communications/Projects/Completed\ Projects/

# Find a folder with a specified string (e.g. "Academics") in its name.
# Move (not copy) the folder to its corresponding sub-folder of the Archived Projects folder. (e.g. /Academics)

for folder in *; do
    if [[ -d "$folder" ]]; then
        if [[ "$folder" == *Academics* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Academics/
        fi
        elif [[ "$folder" == *Admissions* ]]; then
            echo "Archiving $folder to Archived Projects → Admissions...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Admissions/
        fi
        elif [[ "$folder" == *Alumni* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Alumni/
        fi
        elif [[ "$folder" == *Communications* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Communications/
        fi
        elif [[ "$folder" == *Development* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Development/
        fi
        elif [[ "$folder" == *President* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/President/
        fi
        elif [[ "$folder" == *Student\ Life* ]]; then
            echo "Archiving $folder to Archived Projects → Academics...";
            mv "$folder" /Volumes/communications/Projects/Archived\ Projects/Student\ Life/
        fi
    else #Folders that don't match the pattern prompt the use to move them by hand.
        echo "$folder does not have a Department name. Move it by 
done

Il mio problema

Il mio script potrebbe analizzare in modo errato e archiviare erroneamente un progetto denominato [2006.03.01] - Print - Development - Academics and Accreditation - D0601-08 . Avrebbe letto "Accademici" prima che arrivasse al condizionale per il cliente "Sviluppo". Di conseguenza, sarebbe file in "Academics". E dovrei riprenderlo a mano!

Il vantaggio del mio sistema

I miei colleghi e io siamo stati scrupolosi riguardo alla nostra nomenclatura (descritta sopra). So che il nome del cliente rientra tra il 2 ° e il 3 ° trattino.

La mia domanda

Come sfruttare il vantaggio del mio sistema per risolvere il mio problema? Voglio che questo script corrisponda solo alla parte del nome della cartella che viene dopo i primi due trattini e prima del terzo trattino, cioè, voglio solo che questo script cerchi nel "campo" del client nella cartella nome. Continuo a pensare "espressioni regolari" ma non ho idea di come implementarle.

Nota: preferisco che una soluzione aumenti il mio script corrente, piuttosto che sostituirlo. Sono arrivato su @patrix su questo sito e la sua idea ha aggirato alcuni errori.

    
posta Crowder 22.05.2014 - 22:43
fonte

3 risposte

3

Ci sono diversi modi per farlo in bash e amici (potresti davvero buttarti fuori usando sed o awk ). Un modo piuttosto semplice è usare cut per ottenere il nome della cartella

if [[ -d "$folder" ]]; then
    target=$(echo $(echo "$folder" | cut -d- -f 3))
    echo "Archiving $folder to Archived Projects → $target...";
    mv "$folder" /Volumes/communications/Projects/Archived\ Projects/$target/
fi

Il $(echo $(echo ... )) è un approccio pigro per sbarazzarsi dello spazio iniziale / finale (perché cut non supporta i delimitatori multi-char).

Se vuoi buttarti fuori con sed puoi usare

    target=$(echo "$folder" | sed -n 's/^[^\-]*-[^\-]*- \([^\-]*\) -.*//p')

invece di cut . Funziona solo se il nome della cartella di destinazione non contiene un - stesso.

Invece della corrispondenza dei pattern puoi anche usare una funzione di shell per incapsulare la maggior parte della complessità.

#!/bin/bash

function checkAndMove() {
    if [[ "$1" == *$2* ]]; then
        echo "Archiving $1 to Archived Projects → $2...";
        mv "$1" /Volumes/communications/Projects/Archived\ Projects/$2/
    fi
}

cd /Volumes/communications/Projects/Completed\ Projects/

for folder in *; do
    if [[ -d "$folder" ]]; then
        checkAndMove Academics
        checkAndMove Admissions
        ...
    fi
done
    
risposta data 23.05.2014 - 07:48
fonte
3

Che ne dici di usare awk con l'opzione field separator -F e separare il campo con il trattino. Quindi prendi il terzo campo.

UPDATE

Ho aggiornato il codice per utilizzare il risultato restituito da awk per posizionare la cartella di destinazione. Ciò consente di risparmiare molto codice. E ha anche usato il separatore "-" come sottolineato da Ian C nei commenti.

#!/bin/bash

# Go to the Completed Projects folder.
cd /Volumes/communications/Projects/Completed\ Projects/

# Find a folder with a specified string (e.g. "Academics") in its name.
# Move (not copy) the folder to its corresponding sub-folder of the Archived Projects folder. (e.g. /Academics)

for folder in *; do
    if [[ -d "$folder" ]]; then
        thirdfield='echo "$folder" | /usr/bin/awk -F ' - ' '{print $3}'';
        echo "Archiving $folder to Archived Projects → $thirdfield...";
        mv "$folder" /Volumes/communications/Projects/Archived\ Projects/"$thirdfield"/"$folder"    
    fi     
done

Ho anche aggiunto / "$ folder" alla fine dello spostamento in modo tale da spostare la cartella stessa. puoi cambiarlo se non è quello che vuoi rimuovendo la "cartella $" dalla fine del comando mv.

È inoltre possibile eseguire il controllo incrociato su un array di 7 nomi in modo che solo le cartelle corrispondenti vengano spostate. (puoi inserire un'altra istruzione dove necessario)

#!/bin/bash

# Go to the Completed Projects folder.
cd /Volumes/communications/Projects/Completed\ Projects/

# Find a folder with a specified string (e.g. "Academics") in its name.
# Move (not copy) the folder to its corresponding sub-folder of the Archived Projects folder. (e.g. /Academics)

# Array of names to check against
ArrayName=(Academics Admissions  Alumni Communications Development President Student)

for folder in *; do
    if [[ -d "$folder" ]]; then
        thirdfield='echo "$folder" | /usr/bin/awk -F ' - ' '{print $3}'';

        for var in "${ArrayName[@]}"; do
            # Only move the folder if its key name exists in the arrary
            if [ "${var}" = "$thirdfield" ]; then
                echo "Archiving $folder to Archived Projects → $thirdfield...";
                mv "$folder" /Volumes/communications/Projects/Archived\ Projects/"$thirdfield"/"$folder"   
            fi
        done
    fi
done
    
risposta data 23.05.2014 - 01:33
fonte
0

Se puoi imparare bash, puoi certamente imparare una lingua migliore come Ruby per risolvere questo problema.

C'è un ampio margine di miglioramento in ciò che sto postando, ma qui c'è un po 'di base di Ruby che ti fa la riclassificazione per te. Alcuni vantaggi di questo codice Ruby rispetto al codice bash:

  1. Gestisce l'aggiunta di nuovi campi client e li sposta automaticamente in base allo schema di archiviazione preferito
  2. Crea directory intermedie se non esistono
  3. Si interrompe se si verifica un problema durante lo spostamento di una directory che implica che se non si ferma, tutto viene spostato correttamente

E, naturalmente, se me lo chiedi, è infinitamente più leggibile ed espandibile. Se riesci a imparare bash, Ruby è molto più lungo e scoprirai che puoi automatizzarti meglio di quello che puoi con bash.

Ho cercato di rimanere vicino a come funziona la tua bash in modo che sembrasse familiare. Come puoi vedere, è un po 'più tardo di quel bash.

#!/usr/bin/env ruby

require 'fileutils'

SOURCE = '/Users/ianc/tmp/ad'
DESTINATION = '/Users/ianc/tmp/ad-new'

Dir.chdir(SOURCE)

Dir['**'].each do |f|
  if File.exists?(f) && File.directory?(f)
    # Format: [YYYY.MM.DD] - Medium - Client - Project name - details--details - JobNumber
    date, medium, client, project, details, job_number = f.split(' - ', 6)
    if client
      destination = File.join(DESTINATION, client)
      FileUtils.mkpath destination if !File.exists?(destination)
      destination = File.join(destination, f)
      source = File.join(SOURCE, f)
      puts 'Moving: ' + source + ' --> ' + destination
      FileUtils.mv(source, destination)
    else
      puts 'Skipping: ' + f
    end
  end
end
    
risposta data 23.05.2014 - 03:50
fonte

Leggi altre domande sui tag