Скрипт MacOS для перемещения файлов по тегу - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь написать сценарий оболочки, чтобы я мог перемещать школьные файлы из одного места назначения в другое на основе ввода. Я загружаю эти файлы из источника, такого как canvas, и хочу переместить их из своих загрузок, основываясь на назначенном мной теге, в путь к папке моего курса, который достаточно глубоко вложен благодаря тому, как я организован. К сожалению, поскольку я храню эти файлы в своей учетной записи OneDrive, я не могу устранить некоторые пробелы, но я считаю, что я их учел. Прямо сейчас скрипт выглядит следующим образом:

if [ "$1" = "311" ];
then
    course="'/path/to/311/folder/$2'"

elif [ "$1" = "411" ];
then
    course="'/path/to/411/folder/$2'"

elif [ "$1" = "516" ];
then
    course="'/path/to/516/folder/$2'"

elif [ "$1" = "530" ];
then
    course="'/path/to/530/folder/$2'"

elif [ "$1" = "599" ];
then
    course="'/path/to/599/folder/$2'"

fi

files=$(mdfind 'kMDItemUserTags='$1'' -onlyin /Users/user/Downloads)
#declare -a files=$(mdfind 'kMDItemUserTags='$1'' -onlyin /Users/user/Downloads)
#mv $files $course
#echo "mv $files $course"
#echo $course

for file in $files
#for file in "${files[@]}"
do 
    #echo $file
    #echo $course
    mv $file $course
done

Где $ 1 - это идентификатор тега и первая часть выбора пути, а $ 2 - это папка с номером недели, в которую я хочу ее переместить. Одиночные кавычки предназначены для указания расстояния в пути к файлу. Я мог бы очень легко сделать это в Python, но я пытаюсь расширить свои возможности. Каждый раз, когда я запускаю этот скрипт, я получаю следующее сообщение:

usage: mv [-f | -i | -n] [-v] source target
       mv [-f | -i | -n] [-v] source ... directory

Сначала я попытался переместить их все сразу (в соответствии с первой закомментированной командой mv) и получил эту ошибку, затем попробовал цикл for и массив, но каждый раз получал одну и ту же ошибку. Однако, когда я раскомментирую операторы echo в цикле for и вручную пытаюсь переместить каждый из них, копируя и вставляя пути в командную строку, это работает отлично. Мое лучшее предположение - что-то делать с форматированием переменной "files", так как

echo "mv $files $course"

указывает на наличие символа новой строки или разделителя между каждым сохраняемым файлом.

Я уверен, что это что-то очень простое, что мне не хватает, так как я только начал пытаться подобрать сценарии оболочки на прошлой неделе, но ничего, что я не смог найти в Интернете, не помогло мне решить эту проблему. Любая помощь будет принята с благодарностью. Спасибо

Ответы [ 3 ]

0 голосов
/ 06 января 2019

Вы довольно озадачены тем, как работает цитирование в оболочке. Первое правило: кавычки идут вокруг данных, а не в данных. Например, вы используете:

course="'/path/to/311/folder/$2'"
...
mv $file $course

Когда вы устанавливаете course таким образом, двойные кавычки обрабатываются как синтаксис оболочки (т. Е. Они изменяют то, что между ними анализируется), но одинарные кавычки сохраняются как часть значения переменной и будут после этого рассматриваться как данные. Когда вы используете эту переменную в команде mv, она на самом деле ищет каталог, буквально названный одинарной кавычкой, и под этим каталогом, называемым «путь» и т. Д. Вместо этого, просто поместите соответствующие кавычки для того, как вы хотите, чтобы он анализировался в этой точке , а затем двойные кавычки вокруг переменной при ее использовании (для предотвращения вероятного нежелательного расщепления слов и расширения по шаблону). Как это:

course="/path/to/311/folder/$2"
...
mv "$file" "$course"    # This needs more work -- see below

Кроме того, где у вас есть:

mdfind 'kMDItemUserTags='$1'' -onlyin /Users/user/Downloads

это не имеет никакого смысла. У вас есть раздел с одинарными кавычками, 'kMDItemUserTags=', где кавычки не действуют вообще (одинарные кавычки подавляют все специальные значения, которые имеют символы, например $, вводящие подстановку переменных, но там нет символов с специальные значения, поэтому нет причины для кавычек), за которыми следуют $ без двойных кавычек вокруг него, что означает, что некоторые специальные символы (пробелы и подстановочные знаки) в его значении получат специальный синтаксический анализ (который вам, вероятно, не нужен), за которым следует строка в одинарных кавычках нулевой длины, '', которая разбирается на абсолютно ничего. Вы хотите часть $1 в двойных кавычках; некоторые люди также включают остальную часть строки в раздел в двойных кавычках, что никак не влияет. Фактически, кроме части $2 (и пробелов между параметрами), вы можете заключать в кавычки или нет, как вы хотите. Таким образом, любой из них будет работать эквивалентно:

mdfind kMDItemUserTags="$1" -onlyin /Users/user/Downloads
mdfind "kMDItemUserTags=$1" -onlyin /Users/user/Downloads
mdfind "kMDItemUserTags=$1" '-onlyin' '/Users/user/Downloads'
mdfind 'kMDItemUserTags'="$1" '-'"only"'in' /'Users'/'user'/'Down'loads
...etc

Хорошо, следующая проблема: парсинг вывода из mdfind из серии символов в отдельные пути к файлам. Это на самом деле сложно. Если вы поместите двойные кавычки вокруг изменяющейся строки, она будет рассматриваться как один длинный путь к файлу, который содержит в себе некоторые символы новой строки (что совершенно законно, но не то, что вы хотите). Если вы не заключите его в двойные кавычки, он будет разбит на отдельные пути к файлам на основе пробелов (не только переводы строк, но также пробелы и табуляции - и пробелы являются общими в именах файлов MacOS), и все, что выглядит как подстановочный знак, будет расширено до списка совпадающих имен файлов. Это может привести к хаосу.

Решение: есть один символ, который не может встречаться в пути к файлу, ASCII NULL (код символа 0), и mdfind -0 выведет свой список, разделенный нулевыми символами. Вы не можете поместить результат в переменную оболочки (они также не могут содержать нули), но вы можете передать его через конвейер, скажем, xargs -0, который (благодаря опции -0) будет анализировать обнуляет как разделители и строит команды из результатов. Есть одна немного хитрая вещь: вы хотите, чтобы xargs поместил пути к файлам, которые он получает в середине списка аргументов, в mv, а не в конце, как это обычно делается. Опция -J позволяет указать, куда добавлять аргументы. Я также предложу две меры безопасности: опция -p для xargs заставляет ее спрашивать перед фактическим выполнением команды (используйте ее, по крайней мере, до тех пор, пока вы не будете уверены, что она работает правильно), и опция -n для mv, который говорит, что не следует перезаписывать существующие файлы в случае конфликта имен. Результат примерно такой:

mdfind -0 kMDItemUserTags="$1" -onlyin /Users/user/Downloads | xargs -0 -p -J% mv -n % "$course"
0 голосов
/ 06 января 2019

Рекомендуется рассмотреть имена файлов с пробелами. Однако проблема в том, что вы не цитируете имя файла в команде mv. Пожалуйста, взгляните на простой пример ниже:

filename="with space.txt"
  => assign a variable to a filname with a space
touch "$filename"
  => create a file "with space.txt"
str="'$filename'"
  => wrap with single quotes (as you do) 
echo $str
  => yields 'with space.txt' and may look good, which is a pitfall
mv $str "newname.txt"
  => causes an error

Команда mv выше вызывает ошибку, потому что команда вызывается с три аргумента: mv 'with space.txt' newname.txt. к несчастью предварительное цитирование одинарными кавычками не имеет смысла.

Вместо этого, пожалуйста, попробуйте что-то вроде:

if [ "$1" = "311" ]; then
    course="/path/to/311/folder/$2"
elif [ "$1" = "411" ]; then
    course="/path/to/411/folder/$2"
elif [ "$1" = "516" ]; then
    course="/path/to/516/folder/$2"
elif [ "$1" = "530" ]; then
    course="/path/to/530/folder/$2"
elif [ "$1" = "599" ]; then
    course="/path/to/599/folder/$2"
else
    # illegal value in $1. do some error handling
fi
# the lines above may be simplified if /path/to/*folder/ have some regularity

mdfind "kMDItemUserTags=$1" -onlyin /Users/user/Downloads | while read -r file; do
    mv "$file" "$course"
done
# the syntax above works as long as the filenames do not contain newline characters
0 голосов
/ 06 января 2019

Вы можете заменить присвоение переменной files и цикл for одной командой, сделайте так:

if [ "$1" = "311" ];
then
    course="'/path/to/311/folder/$2'"

elif [ "$1" = "411" ];
then
    course="'/path/to/411/folder/$2'"

elif [ "$1" = "516" ];
then
    course="'/path/to/516/folder/$2'"

elif [ "$1" = "530" ];
then
    course="'/path/to/530/folder/$2'"

elif [ "$1" = "599" ];
then
    course="'/path/to/599/folder/$2'"

fi

mv -t $course $(mdfind 'kMDItemUserTags='$1'' -onlyin /Users/user/Downloads | sed ':a;N;$!ba;s/\n/ /g)

Команда sed ':a;N;$!ba;s/\n/ /g просто заменяет символы новой строки пробелами, а опция -t для mv просто заставляет mv принять пункт назначения в качестве первого аргумента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...