Я создаю сценарий оболочки 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 на этом сайте, и его идея позволила обойти некоторые ошибки.
Есть несколько способов сделать это в 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
Как насчет использования 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" в конце перемещения, чтобы перемещалась сама папка. вы можете изменить это, если это не то, что вы хотите, удалив папку «$» в конце команды 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
awk
определенно путь, если это должно абсолютно оставаться в bash
.' - '
а не просто'-'
Если вы можете изучить bash, вы, безусловно, можете выучить лучший язык, такой как Ruby, для решения этой проблемы.
В том, что я публикую, есть много возможностей для улучшения, но вот базовый Ruby, который сделает вашу перекатегоризацию за вас. Некоторые преимущества этого кода Ruby по сравнению с вашим кодом bash:
client
полей и автоматически перемещает их в соответствии с вашей предпочтительной схемой архивирования.И, конечно же, если вы спросите меня, это бесконечно более удобочитаемо и расширяемо. Если вы можете выучить bash, Ruby будет очень сложным, и вы обнаружите, что с его помощью вы можете автоматизировать лучше, чем с bash.
Я старался придерживаться того, как работает ваш 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
chmod +x <file name>
. Теперь просто введите имя файла, и он запустится. Эта волшебная первая строка !#/usr/bin/env ruby
говорит ОС запустить скрипт с использованием Ruby.details--details
сильно различается от папки к папке. Он часто включает специальные символы, такие как [ ] -
. @IanC.details--details
имени не встречается шаблон '-', он не сломается.details--details
. Должен ли я указывать это выше?
Ян С.
Краудер
мммммм
Джейсон Салаз
*- Academics -*
?Маркханте
КузенКокаин
Ян С.
Краудер