Сценарии оболочки: выберите папку на основе части имени файла

3

Мой проект

Я создаю сценарий оболочки bash для выполнения из терминала. Его целью является архивирование множества папок проекта. Каждая папка соответствует установленной номенклатуре [YYYY.MM.DD] - Medium - Client - Project name - details--details - JobNumber. Например: [2006.02.01] - Print - Development - Appeal I - Kids Art Show Insert - D0601-11. Эти проекты в настоящее время находятся в одной папке. Я хочу отсортировать их по папкам по имени клиента. Есть 7 (внутренних) клиентов, поэтому я использую следующий скрипт оболочки:

#!/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

Моя проблема

Мой скрипт будет неправильно анализировать и подавать проект с именем [2006.03.01] - Print - Development - Academics and Accreditation - D0601-08. Это прочитало бы "Академики" прежде, чем это когда-либо попадало в условное для клиента "Развитие". В результате это будут файлы в «Академики». И мне придется забрать его обратно вручную!

Преимущество моей системы

Мои коллеги и я были очень внимательны к нашей номенклатуре (описано выше). Я знаю, что имя Клиента находится между 2 и 3 дефисами.

Мой вопрос

Как использовать преимущество моей системы для решения моей проблемы? Я хочу, чтобы этот скрипт совпадал только с той частью имени папки, которая идет после первых двух дефисов и до третьего дефиса, т. Е. Я хочу, чтобы только этот скрипт выполнял поиск в «поле» клиента в имени папки. Я продолжаю думать "регулярные выражения", но не знаю, как их реализовать.

Примечание. Я предпочитаю, чтобы решение дополняло мой текущий сценарий, а не заменяло его. Я пришел к этому через @patrix на этом сайте, и его идея обошла некоторые ошибки.

Crowder
источник
1
Почему Баш ? Если бы я мог дать вам сценарий на другом языке, который бы сработал, с вами все будет в порядке?
Ян С.
Хороший вопрос, @IanC. Bash, потому что это все, что я знаю, как использовать с терминалом в Mac OS X.
Crowder
bash является ограниченным языком, так как ОС Unix теперь поставляются с такими языками, как perl python и т. д. Я бы написал что-то длиннее, чем 3-4 строки, поскольку bash плохо себя ведет
Марк
1
*- Academics -*?
Джейсон Салаз,
Я обновил свой ответ
markhunte

Ответы:

3

Есть несколько способов сделать это с bashдрузьями (вы могли бы действительно вырубить себя, используя sedили awk). Довольно простой способ - использовать cutимя папки

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

Это $(echo $(echo ... ))ленивый подход, чтобы избавиться от начального / конечного пространства (потому cutчто не поддерживает разделители с несколькими символами).


Если вы хотите покончить с собой, sedвы можете использовать

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

вместо cut. Это работает, только если имя целевой папки не содержит -самого себя.


Вместо сопоставления с образцом вы также можете использовать функцию оболочки для инкапсуляции большей части сложности.

#!/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
Nohillside
источник
3

Как насчет использования awk с опцией разделителя полей -F и разделения поля дефисом. Тогда получите третье поле.

ОБНОВИТЬ

Я обновил код, чтобы использовать результат, возвращенный из awk, для размещения папки назначения. Это экономит много кода. А также использовал разделитель «-», как указал Ян в комментариях.

#!/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

Я также добавил / "$ folder" в конце перемещения, чтобы сама папка была перемещена. Вы можете изменить это, если это не то, что вы хотите, удалив «$ folder» в конце команды mv.


Вы также можете провести перекрестную проверку по массиву из 7 имен, так что будут перемещены только те папки, которые соответствуют. (Вы можете вставить инструкцию else, где это необходимо)

#!/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
markhunte
источник
awkэто определенно способ пойти, если это должно остаться bash.
Ян С.
Кроме того, я бы разделил ' - 'вместо просто'-'
Ян С.
@IanC. Хороший вопрос, я отрегулирую это. На самом деле, я только что подумал об использовании thiredfield в качестве переменной в папке назначения, чтобы это помогло. (И я вижу, пока я спал, ты сделал именно это :-))
markhunte
0

Если вы можете изучать bash, вы, безусловно, сможете выучить лучший язык, такой как Ruby, чтобы решить эту проблему.

В том, что я публикую, есть масса возможностей для улучшения, но вот несколько базовых Ruby, которые сделают вашу переклассификацию для вас. Некоторые преимущества этого кода Ruby перед вашим кодом bash:

  1. Он обрабатывает добавление новых clientполей и автоматически перемещает их в соответствии с предпочитаемой вами схемой архивирования.
  2. Это делает промежуточные каталоги, если они не существуют
  3. Он останавливается, если возникает проблема с перемещением каталога, что подразумевает, что, если он не останавливается, все прошло успешно

И, конечно, если вы спросите меня, это бесконечно более читабельно и расширяемо. Если вы можете изучать bash, Ruby - это очень сложная задача, и вы обнаружите, что с ней вы можете автоматизировать лучше, чем с 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
Ян С.
источник
То есть, вы говорите, что я могу выполнить скрипт Ruby из терминала Mac OS X так же, как сценарий bash? (Я явно не программист - пока.) И если так, что бы я напечатал в командной строке, чтобы выполнить указанный скрипт Ruby?
Crowder
Сохранить , что в файл, так же , как ваш сценарий оболочки, а затем установить бит выполнения на нем, набрав: chmod +x <file name>. Теперь просто введите имя файла, и он запустится. Эта волшебная первая строка !#/usr/bin/env rubyуказывает ОС запускать скрипт с использованием Ruby.
Ян С.
А у Макса есть Руби из коробки? @IanC.
Crowder
Да. Руби там из коробки.
Ян С.
Скорее всего, этот скрипт сломается, если он встретится с форматом, отличным от описанного в #comment? details--detailsРаздел колеблется в широких пределах из папки в папку. Он часто включает в себя специальные символы, такие как [ ] -. @IanC.
Crowder