Je suis nouveau dans l'écriture de scripts Bash. J'ai essayé de faire un script qui échangera les noms de fichiers des deux fichiers qui lui ont été transmis par l'utilisateur.
Voici ne image des deux versions de mon script jusqu'à présent
Voici le script au format texte avec mv
:
#! /bin/bash
file1=$file1
file2=$file2
echo "write file name :"
read file1 file2
if [[ $file1 = $file2 ]]
then
cp $file1 $file1
mv $file1 $file2
fi
if [[ $file2 = $file1 ]]
then
mv $file2 $file1
fi
Mais ma question est si je peux faire un script qui laisse l’utilisateur écrire 2 noms de fichiers d’abord, puis le script échangera les 2 noms de fichiers
La base de l'échange de noms de fichiers ce que j'ai lu est ceci
cp $file1 temporaryfile
mv $file1 $file2
mv $file2 temporyfile
Il y a quelque temps, j'ai créé une fonction spécialement à cet effet que je garde dans mon .bashrc
et que je peux adapter à un script. Vous devriez tirer parti de paramètres de position pour que les utilisateurs puissent mettre les noms de fichiers en ligne de commande. Voici ma fonction d'origine:
swap_files() {
if [ $# -ne 2 ]
then
echo "Usage: swap_files file1 file2"
else
local TMPFILE=$(mktemp)
mv -- "$1" "$TMPFILE"
mv -- "$2" "$1"
mv -- "$TMPFILE" "$2"
fi
}
Vous pouvez vous débarrasser de la déclaration swap_files(){
, du mot clé local
et de la fermeture de }
, puis le transformer en script. Ajoutez simplement #!/bin/bash
en haut. Certes, il y a des tas de choses qui peuvent être améliorées, mais au niveau de base, c'est aussi simple que d'échanger (ce qui est d'ailleurs enseigné fréquemment en C pour échanger des éléments de tableau, mais ce n'est qu'un sujet tangent).
#!/bin/bash
if [ $# -ne 2 ]
then
printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
TMPFILE=$(mktemp)
mv -- "$1" "$TMPFILE"
mv -- "$2" "$1"
mv -- "$TMPFILE" "$2"
fi
Bien sûr, n'oubliez pas de citer les paramètres de position si les noms de fichiers contiennent des espaces. Ainsi:
swap_files 'file 1' 'file 2'
Notez l'utilisation de --
pour éviter les problèmes de noms de fichiers contenant -
en tête. Un meilleur moyen serait de prendre l'habitude de référencer des fichiers dans le répertoire de travail actuel avec ./
, surtout si vous utilisez globstar *
(globstar n'est pas pertinent dans cette question, mais il vaut la peine de le mentionner si nous ' Parlez des noms de fichiers avec -
). De plus, ./
way est plus portable, car certaines versions de mv
telles que sous FreeBSD ne disposent pas de l'option --
.
Comme suggéré par terdon dans les commentaires, nous pouvons également créer un fichier temporaire dans le dossier parent du premier fichier pour éviter le déplacement de fichiers entre systèmes de fichiers.
#!/bin/bash
if [ $# -ne 2 ]
then
printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
file1_dir=${1%/*}
# just in case there were no slashes removed, assume cwd
if [ "$file1_dir" = "$1" ]; then
file1_dir="."
fi
tmpfile=$(mktemp -p "$file1_dir" )
mv -- "$1" "$tmpfile"
mv -- "$2" "$1"
mv -- "$tmpfile" "$2"
fi
file1=$file1
file2=$file2
Cette partie attribue la variable $file1
à ... file1
variable; Il y a deux problèmes avec ceci: l'affectation d'une variable à elle-même est redondante, et elle n'existe pas pour commencer, il n'y a pas de déclaration de cette variable plus tôt dans le script.
Voici ce qui se passera si votre utilisateur tente de mettre des éléments même entre guillemets dans votre commande read
:
$ read file1 file2
'one potato' 'two potato'
$ echo "$file1"
'one
$ echo "$file2"
potato' 'two potato'
Conformément au comportement du shell, le shell scinde tout ce qui est lu sur stdin
et tente d'adapter chaque mot aux variables correspondantes. Si les mots dépassent le nombre de variables, il essaie de tout insérer dans la dernière variable. Je vous recommande de lire chaque fichier, un à la fois.
Tu fais
cp $file1 $file1;
Ça va produire une erreur
$ cp input.txt input.txt
cp: 'input.txt' and 'input.txt' are the same file
Peut-être que tu voulais faire
cp "$file1" "$file1".tmp
Ou utilisez simplement la commande mktemp
comme je le faisais. Notez également la citation de variables pour éviter le fractionnement de Word.
Saviez-vous que vous pouvez classer n'importe quel fichier avec redirection pour effectuer une copie? Donc, utiliser mv
ou cp
n'est pas le seul moyen. Quelque chose comme ça:
$ cat ./wallpaper.jpg > wallpaper.jpg.tmp
$ cat ./screenshot.jpg > wallpaper.jpg
$ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
Vous pouvez utiliser le développement de paramètres pour la tâche si vous obtenez vos deux noms de fichiers ou vous pouvez les lire dans le script. L'exemple de script ci-dessous utilise le développement de paramètres. Et vous voudrez peut-être utiliser un répertoire temporaire pour vos options de déplacement, car si le nom de fichier utilisé dans le script existe déjà, ce fichier sera automatiquement remplacé.
#!/bin/bash
# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)
# copying the filenames into variables for later use
file1="$1"
file2="$2"
# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$1" $TMP/tempfile
# renaming file2 to file1 name
mv "$2" "$file1"
# now renaming and moving the tempfile to file2
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP
De Bash-Manual # Shell-Parameters :
Un paramètre est une entité qui stocke des valeurs. Il peut s'agir d'un nom, d'un numéro ou de l'un des caractères spéciaux répertoriés ci-dessous. Une variable est un paramètre désigné par un nom. Une variable a une valeur et zéro ou plusieurs attributs. Les attributs sont attribués à l'aide de la commande declare builtin (voir la description de la déclaration dans les Bash Builtins).
Un paramètre est défini si une valeur lui a été attribuée. La chaîne nulle est une valeur valide. Une fois qu'une variable est définie, elle peut être désactivée uniquement à l'aide de la commande intégrée unset.
Une variable peut être assignée à un énoncé de la forme
name = [ valeur ]
Et si vous voulez lire les noms de fichiers à partir d’un dialogue interactif:
#!/bin/bash
# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)
# reading the filenames from user input into variables for later use
read -rp "Enter first filename: " file1
read -rp "Enter second filename: " file2
# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$file1" $TMP/tempfile
# renaming file2 to file1 name
mv "$file2" "$file1"
# now renaming and moving the tempfile to file2
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP
De Bash-Manual # Bash-Builtins :
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p Prompt] [-t timeout] [-u fd] [name …]
Une ligne est lue à partir de l'entrée standard ou du descripteur de fichier fd fourni en tant qu'argument de l'option
-u
, divisé en mots en tant que décrit ci-dessus dans [Fractionnement des mots] [6], et le premier mot est attribué au premier nom , le deuxième mot au deuxième nom , etc. S'il y a plus de mots que de noms, les mots restants et leurs délimiteurs intermédiaires sont attribués au dernier nom . S'il y a moins de mots lus dans le flux d'entrée que de noms, les noms restants se voient attribuer des valeurs vides. Les caractères de la valeur de la variableIFS
servent à diviser la ligne en mots en utilisant les mêmes règles que le shell utilise pour le développement (décrites ci-dessus dans [Fractionnement de mots] [6]). Le caractère de barre oblique inversée '\
' peut être utilisé pour supprimer toute signification particulière du prochain caractère lu et du maintien de la ligne. Si aucun nom n'est fourni, la ligne lue est affectée à la variableREPLY
.
read
accepte plusieurs options. Dans ce cas, deux sont les plus pertinents, car vous souhaitez poser une question à l'utilisateur et obtenir ses commentaires. Ces options sont:
-r
→ Si cette option est donnée, la barre oblique inverse ne fait pas office de caractère d'échappement. La barre oblique inverse est considérée comme faisant partie de la ligne. En particulier, une paire backslash-newline ne peut pas être utilisée comme continuation de ligne.-p Prompt
→ Afficher Invite , sans nouvelle ligne, avant d'essayer de lire une entrée. L'invite ne s'affiche que si l'entrée provient d'un terminal.
Bien que ce ne soit pas la pire situation à oublier -r
, vous souhaitez presque toujours l’inclure pour empêcher \
d’agir en tant que caractère d'échappement. -p
montre à l'utilisateur une invite.