MacOS Shell脚本基于标签移动文件

时间:2019-01-06 01:30:40

标签: bash macos shell

我正在尝试编写一个shell脚本,以便可以根据输入将学校文件从一个目标移动到另一个目标。我从诸如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是标记ID和路径选择的第一部分,$ 2是我要将其移动到的星期几文件夹。单引号用于处理文件路径中的间距。我可以很轻松地在python中执行此操作,但我正在尝试扩展某些功能。每次运行此脚本时,都会收到以下消息:

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

我最初尝试一次将它们全部移动(按照第一个被注释掉的mv命令)并得到此错误,然后尝试for循环和数组,但每次都得到相同的错误。但是,当我在for循环中取消注释echo语句并手动尝试通过将路径复制并粘贴到命令行来移动每个语句时,它可以完美地工作。我最好的猜测是与变量“文件”的格式有关,因为

echo "mv $files $course"

表示它保存的每个文件之间都有换行符或分隔符。

我敢肯定,自从上周我开始尝试使用Shell脚本以来,我一直缺少这种超级简单的东西,但是我在网上找不到的任何东西都可以帮助我解决这个问题。任何帮助将不胜感激。谢谢

3 个答案:

答案 0 :(得分:0)

您可以替换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的{​​{1}}选项仅使mv以目的地作为第一个参数。

答案 1 :(得分:0)

您对shell中的引用工作方式感到困惑。第一条规则:引号用于 数据,而不是 in 数据。例如,您使用:

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

以这种方式设置course时,双引号被视为shell语法(即,它们改变了它们之间的解析方式),但是单引号被存储为变量值的一部分,并且此后将被视为数据。当您在mv命令中使用此变量时,它实际上是在寻找一个名叫单引号的目录,并在该目录下寻找一个名为“ path”的目录,以此类推。相反,只需对所需的方式加上合适的引号即可。然后在处解析 ,然后在使用该变量时将其双引号引起来(以防止可能不需要的单词拆分和通配符扩展)。像这样:

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文件名中空格是常见的)被分成单独的文件路径, em>和任何看起来像通配符的东西都将扩展为匹配文件名的列表。这容易引起混乱。

解决方案:文件路径中不能出现一个字符,即ASCII NULL(字符代码0),而mdfind -0将输出以空字符分隔的列表。您不能将结果放入shell变量中(它们也不能包含null),但可以通过管道将其传递到xargs -0,这将((由于-0选项)将null解析为定界符,并根据结果构建命令。有一点棘手的事情:您希望xargs将其在参数列表中间的文件路径放入mv,而不是像通常那样放在结尾。使用-J选项可以告诉它在何处添加参数。我还将建议两种安全措施:-p的{​​{1}}选项会在实际执行命令之前询问(至少使用此命令,直到您确定它做正确的事情为止),以及{ xargs的{​​1}}选项,它告诉它在命名冲突时不要覆盖现有文件。结果是这样的:

-n

答案 2 :(得分:0)

考虑带空格的文件名是一个很好的观点。 但是,问题是您没有在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