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
command-line
bash
scripts
swap
DeadPool69
quelle
quelle
file1=$file1
? Meinten Siefile1=$1
?cp $file1 $file1
- Sie kopieren eine Datei in sich selbst.Antworten:
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
.bashrc
und 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:Sie können
swap_files(){
Deklaration,local
Schlüsselwort und Abschluss entfernen}
und in ein Skript umwandeln - fügen Sie es einfach#!/bin/bash
oben 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).Denken Sie natürlich daran, die Positionsparameter anzugeben, wenn Dateinamen Leerzeichen enthalten. Wie so:
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 Versionenmv
wie 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.
Ihr Skript und Dinge zu verbessern
1. Redundante Variablenzuweisung
Dieser Teil weist
$file1
...file1
Variable 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
read
Befehl einzufügen:Entsprechend dem Shell-Verhalten teilt die Shell alles, was gelesen wird, auf
stdin
und 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
Das wird einen Fehler erzeugen
Vielleicht wolltest du es tun
Oder nutzen Sie einfach den
mktemp
Befehl 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
mv
odercp
nicht der einzige Weg. Etwas wie das:quelle
mv
sie. Für eine weitere Verbesserung können Sie außerdemdir1="${file1%/*}"; tmpfile="$(mktemp -p "$dir1")"
festlegen, dass die Tempfile immer im selben Dateisystem wie die Originaldatei erstellt wird, um zu vermeiden, dass siemv
über Dateisystemgrenzen hinweg ausgeführt wird.mktemp
niemals Whitespaces und so etwas, daher ist ein Zitieren$TMPFILE
nicht erforderlich. Andererseits empfiehlt es sich, nur einen Pfad und einen Dateinamen zu zitieren, die in einem Skript vorkommen.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.
Aus dem Bash-Handbuch # Shell-Parameter :
Und wenn Sie die Dateinamen aus einem interaktiven Dialog einlesen möchten:
Aus dem Bash-Handbuch # Bash-Builtins :
read
akzeptiert 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: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.-p
zeigt dem Benutzer eine Eingabeaufforderung.quelle
#!
tatsächlich erlaubt , siehe unix.stackexchange.com/q/276751/85039 Ansonsten gute Antwortmv $TMP/tempfile "$file2" &&
würde verhindern,$TMP
dass es entfernt wird, fallsmv
es 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!"
?$1
und$2
in Ihrem zweiten Skript ... Übrigens, wozu$file1
und$file2
im ersten?