Pour une mission, on me demande d'écrire intelligemment une fonction bash qui a la même fonctionnalité de base que la fonction cp
(copie). Il n'a qu'à copier un fichier dans un autre, donc pas de fichiers multiples copiés dans un nouveau répertoire.
Comme je suis nouveau dans la langue bash, je ne comprends pas pourquoi mon programme ne fonctionne pas. La fonction d'origine demande d'écraser un fichier s'il est déjà présent, j'ai donc essayé de l'implémenter. Il échoue.
Il semble que le fichier échoue sur plusieurs lignes, mais surtout à la condition où il vérifie si le fichier à copier existe déjà ( [-e "$2"]
). Même ainsi, il affiche toujours le message qui est censé être déclenché si cette condition est remplie (le nom du fichier ...).
Quelqu'un pourrait-il m'aider à corriger ce fichier, fournissant éventuellement des informations utiles dans ma compréhension de base de la langue? Le code est comme suit.
#!/bin/sh
echo "file variable: $2"
if [-e file]&> /dev/null
then
echo "The file name already exists, want to overwrite? (yes/no)"
read | tr "[A-Z]" "[a-z]"
if [$REPLY -eq "yes"] ; then
rm "$2"
echo $2 > "$2"
exit 0
else
exit 1
fi
else
cat $1 | $2
exit 0
fi
[ ... ] &> /dev/null
. Les tests supportés par[ ... ]
sont toujours silencieux, c'est uniquement en cas d'erreurs de syntaxe que toute sortie est produite. Faire taire un tel test, c'est simplement creuser votre propre tombe.cat $1 | $2
"redirige" la sortie de la première commande vers la commande de votre deuxième variable. Mais c'est un nom de fichier, pas le nom d'une commande à exécuter.[
commande dans votreif
.|
et la redirection vers un fichier avec>
. Ce sont des problèmes de syntaxe de base qui auraient déjà dû être traités dans votre classe.Réponses:
L'
cp
utilitaire remplacera volontiers le fichier cible si ce fichier existe déjà, sans inviter l'utilisateur.Une fonction qui implémente une
cp
capacité de base , sans utilisercp
seraitSi vous souhaitez inviter l'utilisateur avant d'écraser la cible (notez qu'il peut ne pas être souhaitable de le faire si la fonction est appelée par un shell non interactif):
Les messages de diagnostic doivent aller dans le flux d'erreur standard. C'est ce que je fais avec
printf ... >&2
.Notez que nous n'avons pas vraiment besoin
rm
du fichier cible car la redirection le tronquera. Si nous ne voulonsrm
d'abord, alors vous devriez vérifier si elle est un répertoire, et si elle est, placez le fichier cible à l' intérieur dans le répertoire correspondant , juste la façon dont lecp
ferais. Cela fait cela, mais toujours sans expliciterm
:Vous pouvez également vous assurer que la source existe réellement, ce qui est le
cp
cas (lecat
fait aussi, donc il peut être complètement ignoré, mais cela créerait un fichier cible vide):Cette fonction n'utilise pas de "bashismes" et devrait fonctionner dans des
sh
coques similaires.Avec un peu plus de réglages pour prendre en charge plusieurs fichiers source et un
-i
indicateur qui active l'invite interactive lors du remplacement d'un fichier existant:Votre code a de mauvais espacements
if [ ... ]
(besoin d'espace avant et après[
et avant]
). Vous ne devez pas non plus essayer de rediriger le test vers/dev/null
car le test lui-même n'a pas de sortie. Le premier test doit en outre utiliser le paramètre positionnel$2
, pas la chaînefile
.En utilisant
case ... esac
comme je l'ai fait, vous évitez d'avoir à minuscules / majuscules la réponse de l'utilisateur qui utilisetr
. Dansbash
, si vous aviez voulu le faire de toute façon, une manière moins coûteuse de le faire aurait été d'utiliserREPLY="${REPLY^^}"
(pour les majuscules) ouREPLY="${REPLY,,}"
(pour les minuscules).Si l'utilisateur dit "oui", avec votre code, la fonction met le nom de fichier du fichier cible dans le fichier cible. Ce n'est pas une copie du fichier source. Il doit passer par le bit de copie réel de la fonction.
Le bit de copie est quelque chose que vous avez implémenté à l'aide d'un pipeline. Un pipeline est utilisé pour transmettre des données de la sortie d'une commande à l'entrée d'une autre commande. Ce n'est pas quelque chose que nous devons faire ici. Appelez simplement
cat
le fichier source et redirigez sa sortie vers le fichier cible.La même chose ne va pas lorsque vous appelez
tr
plus tôt.read
définira la valeur d'une variable, mais ne produit aucune sortie, donc le raccordementread
à n'importe quoi est absurde.Aucune sortie explicite n'est nécessaire à moins que l'utilisateur ne dise "non" (ou que la fonction rencontre une condition d'erreur comme dans les bits de mon code, mais puisque c'est une fonction que j'utilise
return
plutôt queexit
).Vous avez également dit "fonction", mais votre implémentation est un script.
Jetez un œil à https://www.shellcheck.net/ , c'est un bon outil pour identifier les bits problématiques des scripts shell.
L'utilisation
cat
n'est qu'une façon de copier le contenu d'un fichier. D'autres moyens incluentdd if="$1" of="$2" 2>/dev/null
sed "" "$1" >"2"
ouawk '1' "$1" >"$2"
outr '.' '.' <"$1" >"$2"
etc.L'astuce consiste à faire en sorte que la fonction copie les métadonnées (propriété et autorisations) de la source vers la cible.
Une autre chose à noter est que la fonction que j'ai écrite se comportera très différemment
cp
si la cible est quelque chose comme/dev/tty
par exemple (un fichier non régulier).la source
cat "$1" >"$2"
fonctionnera en 2 fois: le shell voit d'abord> "$2"
, ce qui signifie: créez-ou-clobber (= vide) le fichier "$ 2", et redirigez la sortie standard de la commande vers ce fichier. Ensuite, il lance la commandecat "$1"
:, et redirige sa sortie standard vers un fichier"$2"
(déjà vidé ou créé vide).-ef
comparaisons de fichiers etlocal -a
.-ef
prend note si les deux fichiers sont identiques (prend également en charge les liens) etlocal -a
crée une variable de tableau local.