En tant que personne essayant d'apprendre bash, j'ai créé des scripts simples pour tester diverses commandes. Dans le script simple suivant, j'expérimente avec la commande de lecture.
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
cp $file_to_copy /tmp
Si l'utilisateur entre l'intégralité du chemin d'accès à un fichier de test, par exemple,/home/$ USER/test à l'invite, le script s'exécute comme prévu et crée une copie de "test" dans le répertoire/tmp. Cependant, si l'utilisateur entre le raccourci ~/test, le terminal renvoie l'erreur cp: impossible de stat '~/test': aucun fichier ou répertoire de ce type. Pourquoi la commande cp ne parvient-elle pas à trouver une représentation équivalente du chemin du fichier une fois entré?
Si j'entre indépendamment dans le terminal:
cp ~/test /tmp
le fichier de test est copié sans incident, il s'agit donc probablement d'une nuance d'affectation de variable que je me suis trompée, mais que je ne peux pas comprendre.
Merci.
Ce qui se passe, c’est que vous demandez littéralement à Shell de trouver ~/test
à partir du répertoire dans lequel le script se trouve actuellement. En d’autres termes, si vous exécutez à partir de /home/$USER
, il recherchera le fichier test
in sous-répertoire nommé ~
, pas le développement /home/$USER/test
.
Ce que vous pouvez utiliser, c’est l’évaluation intégrée de bash
, qui prend tous les arguments, les regroupe en une seule commande et laisse le shell l’exécuter. Maintenant, Shell développera ~
.
xieerqi:
$ ./copyScript.sh
Enter a path to a file: ~/test
Entered /home/xieerqi/test
xieerqi:
$ ls -l /tmp/test
-rw-rw-r-- 1 xieerqi xieerqi 0 Dec 21 01:14 /tmp/test
xieerqi:
$ cat copyScript.sh
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
file_to_copy=$(eval echo $file_to_copy)
echo "Entered $file_to_copy"
cp "$file_to_copy" /tmp
Je vous suggérerais également d'utiliser des guillemets autour de la substitution de commande $( . . .)
pour vous assurer que les fichiers contenant des espaces et des caractères spéciaux seront correctement capturés dans la variable file_to_copy
ADDITION
Comme indiqué dans les commentaires, eval
intégré peut être dangereux.
Personnellement, j'utilise assez souvent la commande readlink
dans mes scripts et évite d'utiliser ~
. Et puisque dans ce script, vous demandez quand même une entrée utilisateur, pourquoi ne pas utiliser des arguments en ligne de commande? Ainsi:
xieerqi:
$ ./copyScript.sh testFile.txt
/home/xieerqi/testFile.txt
xieerqi:
$ cat copyScript.sh
#!/bin/bash
FILE="$(readlink -f "$@" )"
echo $FILE
# uncomment for copying
# cp $FILE /tmp
nom de répertoire et nom de base
GNU coreutils fournit deux commandes, basename
et dirname
. basename
affiche le dernier élément du chemin et dirname
tout ce qui précède le dernier élément. On pourrait utiliser le développement de paramètres internes de bash
pour faire la même chose, mais puisque nous avons ces outils - pourquoi ne pas les utiliser:
xieerqi:
$ ./copyScript.sh
Enter path to file: ~/testFile.txt
File entered is /home/xieerqi/testFile.txt
xieerqi:
$ ./copyScript.sh
Enter path to file: ~/sumFile.txt
File entered is /home/xieerqi/sumFile.txt
ERROR, file doesn't exist
xieerqi:
$ cat copyScript.sh
#!/bin/bash
read -p "Enter path to file: " FILEPATH
DIRPATH="$(dirname "$FILEPATH")"
FILENAME="$(basename "$FILEPATH" )"
if [ "$DIRPATH" == "~" ];then
DIRPATH="$HOME"
fi
FILEPATH="$DIRPATH"/"$FILENAME"
echo "File entered is " "$FILEPATH"
[ -e "$FILEPATH" ] || { echo "ERROR, file doesn't exist"; exit 1;}
Essayez ces commandes dans votre script:
#!/bin/bash
read -p "Enter a path to a file: " file_to_copy
file_to_copy=$(bash -c "echo $file_to_copy")
file_to_copy=${file_to_copy/~\//$HOME\/}
cp "${file_to_copy}" /tmp
Explication:
La troisième ligne développera env
valeurs de variable s'il y en a.
La quatrième ligne du code ci-dessus essaiera de rechercher ~
dans votre variable file_to_copy
et si elle est trouvée, elle sera remplacée par $HOME
chemin.
Et \/
ici \
est un caractère de séquence d'échappement inversée pour /
, car nous voulons remplacer ~/
par $HOME/
.