Skript zum Austauschen der Namen zweier Dateien

7

Ich bin neu in Bash Scripting. Ich habe versucht, ein Skript zu erstellen, das die Dateinamen der beiden vom Benutzer übergebenen Dateien austauscht.

Hier ist ein Bild der beiden bisherigen Versionen meines Skripts

Hier ist das Skript im Textformat mit 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

Aber meine Frage ist, ob ich ein Skript erstellen kann, mit dem der Benutzer zuerst 2 Dateinamen aufschreiben kann, dann tauscht das Skript die 2 Dateinamen aus

Die Grundlagen des Austauschs von Dateinamen, die ich gelesen habe, sind folgende

cp $file1 temporaryfile
mv $file1 $file2
mv $file2 temporyfile
DeadPool69
quelle
2
Was ist der Sinn von file1=$file1? Meinten Sie file1=$1?
Barmar
2
cp $file1 $file1- Sie kopieren eine Datei in sich selbst.
Barmar
1
Sie haben einfache Anführungszeichen um eine Zeichenfolge mit Variablen. Wissen Sie nicht, dass Variablen nur in doppelte Anführungszeichen gesetzt werden?
Barmar

Antworten:

11

Ein möglicher Weg, dies zu tun

Vor einiger Zeit habe ich eine Funktion speziell für diesen Zweck erstellt, die ich in meinem habe .bashrcund die in ein Skript umgewandelt werden kann. Sie sollten Positionsparameter nutzen, damit Benutzer Dateinamen in die Befehlszeile einfügen können. Hier ist meine ursprüngliche Funktion:

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
}

Sie können swap_files(){Deklaration, localSchlüsselwort und Abschluss entfernen }und in ein Skript umwandeln - fügen Sie es einfach #!/bin/bashoben hinzu. Zugegeben, es gibt Unmengen von Dingen, die verbessert werden können, aber auf der sehr grundlegenden Ebene ist das ungefähr so ​​einfach wie das Austauschen (was in C übrigens häufig gelehrt wird, um Array-Elemente auszutauschen, aber das ist nur ein tangentiales Thema).

#!/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

Denken Sie natürlich daran, die Positionsparameter anzugeben, wenn Dateinamen Leerzeichen enthalten. Wie so:

swap_files 'file 1' 'file 2'

Beachten Sie die Verwendung von --, um Probleme mit Dateinamen zu vermeiden, die dazu führen -. Ein besserer Weg wäre es, sich daran zu gewöhnen, Dateien im aktuellen Arbeitsverzeichnis mit zu referenzieren ./, insbesondere wenn Sie globstar verwenden *(globstar ist in dieser Frage nicht relevant, aber es ist erwähnenswert, wenn es sich um Dateinamen mit führenden Namen handelt -). Außerdem ./ist viel mehr tragbar, da einige Versionen mvwie auf FreeBSD nicht die haben --Option.


Wie von terdon in den Kommentaren vorgeschlagen, können wir auch temporäre Dateien im übergeordneten Ordner der ersten Datei erstellen, um zu vermeiden, dass Dateien zwischen Dateisystemen verschoben werden.

#!/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

Ihr Skript und Dinge zu verbessern

1. Redundante Variablenzuweisung

file1=$file1
file2=$file2

Dieser Teil weist $file1... file1Variable eine Variable zu ; Dies hat zwei Probleme: Das Zuweisen einer Variablen zu sich selbst ist redundant und existiert zunächst nicht. Es gibt keine Deklaration dieser Variablen weiter oben im Skript.

2. Achten Sie beim Lesen auf das Teilen von Wörtern

Folgendes passiert, wenn Ihr Benutzer versucht, selbst zitierte Elemente in Ihren readBefehl einzufügen:

$ read file1 file2
'one potato' 'two potato'

$ echo "$file1"
'one

$ echo "$file2"
potato' 'two potato'

Entsprechend dem Shell-Verhalten teilt die Shell alles, was gelesen wird, auf stdinund versucht, in jedes Wort in entsprechende Variablen zu passen. Wenn Wörter die Anzahl der Variablen überschreiten, versucht sie, alles in die letzte Variable zu verschieben. Ich würde empfehlen, dass Sie jede Datei einzeln einlesen.

3. Das Kopieren in sich selbst ist ein Fehler

Sie gehen

cp $file1 $file1;

Das wird einen Fehler erzeugen

$ cp input.txt input.txt
cp: 'input.txt' and 'input.txt' are the same file

Vielleicht wolltest du es tun

cp "$file1" "$file1".tmp

Oder nutzen Sie einfach den mktempBefehl wie ich. Beachten Sie auch das Zitieren von Variablen, um eine Wortteilung zu verhindern.


Andere lustige Möglichkeiten, es zu tun

Wussten Sie, dass Sie jede Datei mit Umleitung katzen können , um eine Kopie zu erstellen ? Also ist mvoder cpnicht der einzige Weg. Etwas wie das:

$ cat ./wallpaper.jpg > wallpaper.jpg.tmp
$ cat ./screenshot.jpg > wallpaper.jpg
$ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
Sergiy Kolodyazhnyy
quelle
4
Sie behalten die temporäre Datei nicht, sondern mvsie. Für eine weitere Verbesserung können Sie außerdem dir1="${file1%/*}"; tmpfile="$(mktemp -p "$dir1")"festlegen, dass die Tempfile immer im selben Dateisystem wie die Originaldatei erstellt wird, um zu vermeiden, dass sie mvüber Dateisystemgrenzen hinweg ausgeführt wird.
Terdon
1
Nur eine Anmerkung: Tempfiles, die von erstellt wurden, enthalten mktemp niemals Whitespaces und so etwas, daher ist ein Zitieren $TMPFILEnicht erforderlich. Andererseits empfiehlt es sich, nur einen Pfad und einen Dateinamen zu zitieren, die in einem Skript vorkommen.
Dessert
@ Dessert Guter Punkt. Obwohl es mehr zu zitieren gibt als nur Leerzeichen. Sie könnten an unix.stackexchange.com/q/171346/85039
Sergiy Kolodyazhnyy
6

Sie können die Parametererweiterung für die Aufgabe verwenden, wenn Sie Ihre beiden Dateinamen erhalten, oder Sie können sie im Skript einlesen. In meinem folgenden Beispielskript wird die Parametererweiterung verwendet. Möglicherweise möchten Sie ein temporäres Verzeichnis für Ihre Verschiebungsoptionen verwenden, da diese Datei stillschweigend überschrieben wird, wenn der im Skript verwendete Dateiname bereits vorhanden ist.

#!/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

Aus dem Bash-Handbuch # Shell-Parameter :

Ein Parameter ist eine Entität, die Werte speichert. Dies kann ein Name, eine Nummer oder eines der unten aufgeführten Sonderzeichen sein. Eine Variable ist ein Parameter, der durch einen Namen gekennzeichnet ist. Eine Variable hat einen Wert und null oder mehr Attribute. Attribute werden mit dem Befehl declare builtin zugewiesen (siehe Beschreibung der Deklaration in Bash Builtins).

Ein Parameter wird gesetzt, wenn ihm ein Wert zugewiesen wurde. Die Nullzeichenfolge ist ein gültiger Wert. Sobald eine Variable festgelegt wurde, kann sie nur mit dem Befehl unset builtin deaktiviert werden.

Eine Variable kann durch eine Anweisung des Formulars zugewiesen werden

name = [ Wert ]

Und wenn Sie die Dateinamen aus einem interaktiven Dialog einlesen möchten:

#!/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

Aus dem Bash-Handbuch # Bash-Builtins :

read [-ers] [-a aname] [-d delim] [-i text] [-n nchars]
    [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]

Eine Zeile wird aus der Standardeingabe oder aus dem als Argument für die Option bereitgestellten Dateideskriptor fd gelesen -u, in Wörter aufgeteilt, wie oben in [Wortaufteilung] [6] beschrieben, und das erste Wort wird dem Vornamen , dem, zugewiesen zweites Wort zum zweiten Namen und so weiter. Wenn es mehr Worte als Namen sind, bleiben die übrigen Worte und ihre dazwischen liegenden Trennzeichen werden die zuletzt zugewiesenen Namen . Wenn weniger Wörter aus dem Eingabestream gelesen werden als Namen, werden den verbleibenden Namen leere Werte zugewiesen. Die Zeichen im Wert der IFSVariablen werden verwendet, um die Zeile nach denselben Regeln in Wörter aufzuteilen, die die Shell für die Erweiterung verwendet (siehe oben unter [Wortaufteilung] [6]). Der Backslash-Charakter '\'kann verwendet werden, um eine spezielle Bedeutung für das nächste gelesene Zeichen und für die Zeilenfortsetzung zu entfernen. Wenn keine Namen angegeben werden, wird die gelesene Zeile der Variablen zugewiesen REPLY.

readakzeptiert mehrere Optionen. In diesem Fall sind zwei am relevantesten, da Sie dem Benutzer eine Frage stellen und Eingaben für ihn erhalten möchten. Diese Optionen sind:

  • -r→ Wenn diese Option angegeben ist, fungiert der Backslash nicht als Escape-Zeichen. Der Backslash wird als Teil der Zeile betrachtet. Insbesondere darf ein Backslash-Newline-Paar nicht als Zeilenfortsetzung verwendet werden.
  • -p prompt→ Zeigen Sie die Eingabeaufforderung ohne nachgestellten Zeilenumbruch an, bevor Sie versuchen, eine Eingabe zu lesen. Die Eingabeaufforderung wird nur angezeigt, wenn die Eingabe von einem Terminal stammt.

Dies ist zwar nicht die schlimmste Situation -r, die Sie vergessen sollten , aber Sie möchten sie fast immer einbeziehen, um zu verhindern, dass Sie als Fluchtcharakter \fungieren. -pzeigt dem Benutzer eine Eingabeaufforderung.

Videonauth
quelle
1
Leerzeichen sind #!tatsächlich erlaubt , siehe unix.stackexchange.com/q/276751/85039 Ansonsten gute Antwort
Sergiy Kolodyazhnyy
mv $TMP/tempfile "$file2" &&würde verhindern, $TMPdass es entfernt wird, falls mves erfolgreich beendet werden könnte (stellen Sie sich eine vollständige Festplatte vor) - oder wie wäre es [ -e $TMP/tempfile ] && rm -r $TMP || echo "Error!"?
Dessert
1
Es gibt noch $1und $2in Ihrem zweiten Skript ... Übrigens, wozu $file1und $file2im ersten?
Dessert