J'essaie d'écrire un script shell afin de pouvoir déplacer les fichiers scolaires d'une destination à une autre en fonction de l'entrée. Je télécharge ces fichiers à partir d'une source telle que canvas et je souhaite les déplacer de mes téléchargements en fonction de la balise que j'attribue, vers le chemin de mon dossier de cours qui est imbriqué assez profondément grâce à la façon dont je reste organisé. Malheureusement, étant donné que je stocke ces fichiers dans mon compte OneDrive école, je ne parviens pas à éliminer certains problèmes d'espacement, mais je pense avoir pris en compte ces derniers. Pour le moment, le script est le suivant:
echo "mv $files $course"
Où $ 1 est l'ID de balise et la première partie de la sélection du chemin, et $ 2 est le dossier du numéro de semaine vers lequel je veux le déplacer. Les guillemets simples sont là pour prendre en compte l'espacement dans le chemin du fichier. Je pourrais très facilement le faire en python, mais j'essaye d'étendre mes capacités. Chaque fois que j'exécute ce script, j'obtiens le message suivant:
usage: mv [-f | -i | -n] [-v] source target mv [-f | -i | -n] [-v] source ... directory
J'ai d'abord essayé de les déplacer tous en même temps (par la première commande mv commentée) et j'ai obtenu ceci erreur, puis a essayé la boucle for et le tableau, mais obtenez la même erreur à chaque fois. Cependant, lorsque je décommente les instructions echo dans la boucle for et que je tente manuellement de déplacer chacune d'elles en copiant et collant les chemins vers la ligne de commande, cela fonctionne parfaitement. Ma meilleure hypothèse est quelque chose à voir avec le formatage de la variable "files", puisque
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
indique la présence d'un caractère de nouvelle ligne ou d'un séparateur entre chaque fichier enregistré. p>
Je suis sûr que c'est quelque chose de super simple qui me manque depuis que je viens de commencer à essayer de reprendre les scripts shell la semaine dernière, mais rien que j'ai pu trouver en ligne ne m'a aidé à résoudre ce problème. Toute aide serait grandement appréciée. Merci
3 Réponses :
Vous pouvez remplacer l'affectation de variable files
et la boucle for avec une commande en faire le script:
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)
Le sed ': a; N; La commande $! ba; s / \ n / / g
remplace simplement les caractères de nouvelle ligne par des espaces, et l'option -t
pour mv
fait simplement mv
prend la destination comme premier argument.
Vous êtes plutôt confus sur le fonctionnement des citations dans le shell. Première règle: les guillemets vont autour des données, pas dans les données. Par exemple, vous utilisez:
mdfind -0 kMDItemUserTags="$1" -onlyin /Users/user/Downloads | xargs -0 -p -J% mv -n % "$course"
Lorsque vous définissez cours
de cette façon, les guillemets sont traités comme une syntaxe shell (c'est-à-dire qu'ils changent la façon dont ce qui est entre eux est analysé ), mais les guillemets simples sont stockés dans le cadre de la valeur de la variable, et seront ensuite traités comme des données. Lorsque vous utilisez cette variable dans la commande mv
, elle recherche en fait un répertoire nommé littéralement guillemet simple, et sous celui-ci un répertoire nommé "chemin", etc. Au lieu de cela, mettez simplement les guillemets appropriés pour savoir comment vous voulez qu'elle soit analysée à ce stade , puis des guillemets doubles autour de la variable lorsque vous l'utilisez (pour éviter le fractionnement de mots probablement indésirables et l'expansion des caractères génériques). Comme ceci:
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
De plus, là où vous avez:
mdfind 'kMDItemUserTags='$1'' -onlyin /Users/user/Downloads
cela n'a pas vraiment de sens. Vous avez une section entre guillemets simples, 'kMDItemUserTags ='
où les guillemets n'ont aucun effet (les guillemets simples suppriment toutes les significations spéciales des caractères, comme $
introduisant une substitution de variable, mais il n'y a pas de caractères avec des significations spéciales, donc pas de raison pour les guillemets), suivi de $
sans guillemets doubles autour, ce qui signifie que certains caractères spéciaux (espaces blancs et jokers) dans sa valeur recevront une analyse spéciale (ce que vous ne voulez probablement pas), suivis d'une chaîne entre guillemets simples de longueur nulle, '' code>, qui analyse exactement rien. Vous voulez la partie
$ 1
entre guillemets; certaines personnes incluent également le reste de la chaîne dans la section entre guillemets, ce qui n'a aucun effet. En fait, à part la partie $ 2
(et les espaces entre les paramètres), vous pouvez citer ou non comme vous le souhaitez. Ainsi, n'importe lequel de ceux-ci fonctionnerait de manière équivalente:
course="/path/to/311/folder/$2" ... mv "$file" "$course" # This needs more work -- see below
Ok, problème suivant: analyser la sortie de mdfind
d'une série de caractères dans des chemins de fichiers séparés. C'est en fait délicat. Si vous mettez des guillemets doubles autour de la chaîne de resilting, elle sera traitée comme un long chemin de fichier contenant des retours à la ligne (ce qui est totalement légal, mais pas ce que vous voulez). Si vous ne le citez pas deux fois, il sera divisé en chemins de fichiers séparés basés sur des espaces (pas seulement des retours à la ligne, mais aussi des espaces et des tabulations - et les espaces sont courants dans les noms de fichiers macOS), et tout ce qui ressemble à un caractère générique sera développé en une liste de noms de fichiers correspondants. Cela tend à provoquer le chaos.
La solution: il y a un caractère qui ne peut pas apparaître dans un chemin de fichier, l'ASCII NULL (code de caractère 0), et mdfind -0
affichera sa liste délimité par des caractères nuls. Vous ne pouvez pas mettre le résultat dans une variable shell (ils ne peuvent pas non plus contenir de valeurs nulles), mais vous pouvez le passer par un tube vers, par exemple, xargs -0
, ce qui (grâce au -0
option) analyse les valeurs nulles comme des délimiteurs et crée des commandes à partir des résultats. Il y a une chose légèrement délicate: vous voulez que xargs
place les chemins de fichiers qu'il obtient au milieu de la liste d'arguments dans mv
, pas à la fin comme il le fait habituellement. L'option -J
vous permet de lui indiquer où ajouter des arguments. Je vais également suggérer deux mesures de sécurité: l'option -p
de xargs
le fait demander avant d'exécuter réellement la commande (utilisez-la au moins jusqu'à ce que vous soyez sûr de faire le bonne chose), et l'option -n
à mv
, qui lui dit de ne pas écraser les fichiers existants en cas de conflit de nom. Le résultat est quelque chose comme ceci:
course="'/path/to/311/folder/$2'" ... mv $file $course
C'est un bon point à considérer à propos des noms de fichiers avec des espaces.
Cependant, le problème est que vous ne citez pas le nom de fichier dans la commande mv
. Veuillez jeter un œil à un exemple simple ci-dessous:
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
La commande mv
ci-dessus provoque une erreur car la commande est appelée avec
trois arguments comme: mv
'with
space.txt'
newname.txt
. Malheureusement
le pré-guillemet avec des guillemets simples n'a pas de sens.
Essayez plutôt quelque chose comme:
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