Wie werden Befehle in einem Bash-Shell-Skript wiedergegeben, aber nicht ausgeführt?

11

Gibt es eine Möglichkeit, ein Shell-Skript auszuführen , bei dem die Befehle wiederholt werden, ohne sie jedoch tatsächlich auszuführen?

Angenommen, ein Skript entfernt eine Datei, deren Name in einer Variablen gespeichert ist:

#!/bin/bash
set -v
FN="filename"
rm -f ${FN}

Beim Hinzufügen set -vwerden die folgenden Befehle vor der Ausführung wiedergegeben:

$ ./scr.sh
FN="filename"
rm -f ${FN}

Jetzt möchte ich den Ablauf dieses Skripts sehen, ohne die Datei tatsächlich zu entfernen. IOW, ich möchte Auswirkungen auf die externe Umgebung und das Dateisystem verhindern. Ist das möglich?

Ich könnte alle Befehle mit einem echoBefehl umschließen, aber es ist anstrengend für ein langes Skript.

ysap
quelle
Ich bin kein Bash-Scripting-Experte, aber wenn ich dies mit einer anderen Programmiersprache schreiben würde, würde ich alle Befehle in einem Array speichern. Auf diese Weise sollte es einfach sein, die Befehle zu durchlaufen und entweder auszuführen oder auszudrucken.
Hugo der Hungrige

Antworten:

8

Es gibt keine Möglichkeit, ein Skript zu durchlaufen, um zu sehen, wie es ausgeführt wird, ohne dies tatsächlich zu tun. In Ihrem Beispiel gibt es keine ifAnweisungen oder Schleifen. In echten Skripten gibt es jedoch oft viele bedingte Anweisungen. Welcher Zweig genommen wird, hängt oft davon ab, was passiert ist, als die Shell den vorherigen Befehl ausgeführt hat. Wenn der Befehl nicht ausgeführt wird, kann die Shell nicht wissen, welche Ausgabe sie generiert hätte oder wie der Rückkehrcode gewesen wäre, von dem die nächste bedingte Verzweigung oder Zuweisungsanweisung abhängen könnte.

Wenn der Punkt ist, dass Sie ein Skript überprüfen und wissen möchten, was es tut, bevor Sie es ausführen, ist das keine schlechte Idee. Realistisch gesehen ist der beste Weg, dies zu tun, das Durchsuchen der Datei mit lessoder viähnlichem.

Hinzugefügt

Wenn Sie ein Skript entwickeln und es beim ersten Mal durchgehen möchten, die Logik testen, aber keinen Schaden anrichten, wenn Sie einen Fehler haben, kann ich häufig nur Änderungen vornehmen die Aussagen, die Schaden anrichten könnten, indem sie echoauf die Vorderseite geklebt werden .

Dies funktioniert häufig in typischen realen Skripten, da (a) das Generieren der Listen von Elementen, über die Sie iterieren, oder der Wert, auf den Sie eine Variable festlegen, häufig generiert werden kann, ohne das Dateisystem zu ändern, und (b) dies normalerweise ausreicht Angenommen, wenn Sie den Befehl ausführen, ist er erfolgreich.

Nicole Hamilton
quelle
Vielen Dank. Es würde mich überraschen, wenn es einen Weg gibt, genau aus dem von Ihnen genannten Grund, aber ich habe beschlossen, es trotzdem zu versuchen. Der Grund dafür ist, dass ich ein Skript entwickle, das Dateien auf vielen Computern in einem Netzwerk verschiebt und einige Aktionen auf den Remote-Hosts ausführt. Ich möchte einen Ablauf des Skripts sehen, bevor die Änderungen tatsächlich an den Dateisystemen vorgenommen werden. Es wäre genauso mühsam, alle Remote-Computer zu
durchsuchen, um Dateifehler
1
Ein Zusatz: Um das Skript zu durchlaufen, können Sie das Skript starten, mit trap read debugdem nach jeder ausgeführten Zeile ein Lesevorgang ausgeführt wird, und Sie können es dann langsam mit der Eingabetaste durchlaufen (dies ist nützlich, wenn Sie ein wirklich langes Skript haben und testen möchten es zum ersten Mal).
Netigger
8

Ich denke, Sie müssen ein Echo abgeben. Wenn Sie den Befehl jedoch in eine Variable einfügen, können Sie ihn ein- und ausschalten. Der Befehl kann auch eine Funktion sein und so ausgefeilt sein, wie Sie möchten.

#!/bin/bash
echo 'Debug'
  D=echo
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy
echo $'\n\nLive'
  D=
  :>| testy
  ls -l testy
  $D rm -f testy
  ls -l testy

!$ > ./conditional.sh
Debug
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy
rm -f testy
-rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy

Live -rw-rw-r-- 1 nobody nobody 0 2013-01-18 15:28 testy ls: cannot access testy: No such file or directory

user191016
quelle
Ich benötigte Anführungszeichen in den Echo / Don't-Echo-Befehlen und musste sie dann D=evalim Live-Teil verwenden. Aber schöne Antwort!
Jasper
5

Sie können den Fluss mit der Option -x für bash verfolgen, die Befehle werden jedoch weiterhin ausgeführt.

Das Beste, was Sie tun können, ist, einfach ein Echo zu setzen oder die Befehle zu kommentieren, die externe Effekte wie rm haben. Zumindest müssten Sie nicht jede Zeile ändern.

Parkydr
quelle
Vielen Dank. Ich habe diese Option in meiner Frage erwähnt, aber sie löst mein Problem nicht wirklich.
Ysap
Entschuldigung, ich dachte, Sie wollten jede Zeile wiederholen. -X würde dies auf einige Befehle reduzieren und Ihnen die Auswertungen zeigen.
Parkydr
4

Ich kenne keine bestimmte Methode für den Trockenlauf, aber wir können einige Vorsichtsmaßnahmen treffen, um ein unbekanntes Skript zu verstehen.

  1. Verwenden Sie eine Chroot- Umgebung, um Ihr Debug-Skript auszuführen. Es fungiert als Sandbox und bietet Schutz vor dem Löschen / Ändern von Hauptsystemdateien.
  2. Verwenden Sie bash -n <script>für die Syntaxprüfung . Aus dem gnu bash-Handbuch https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html

-n Read commands but do not execute them; this may be used to check a script for syntax errors. This option is ignored by interactive shells.

  1. Lesen Sie das Skript mindestens einmal. Sie können die Echo-Anweisung zum Debuggen verwenden, wie in der Antwort von user191016 angegeben .
    • Achten Sie auf verdächtigen oder gefährdeten Code.
    • zB im Zusammenhang mit rm, Befehlen, die / dev / sda usw. betreffen
  2. Versuchen Sie immer, Skripte als normaler Benutzer auszuführen. Vermeiden Sie es, unbekannte Skripte als Root auszuführen .
  3. Sie können einen Alias ​​definieren , um einen Befehl zu erstellen, mit dem Dateien zu Beginn Ihres Skripts als interaktiv geändert werden können. Beispiel alias rm="rm -i" alias cp="cp -i" alias mv="mv -i" Für Stream-Editoren wie sed sed -i(-i ändert die vorhandene Datei ohne -i führt einen Probelauf für sed durch, überprüfen Sie die Manpage auf Details) kann den vollständigen Befehl kopieren und ausführen. Kurz vor dem eigentlichen Befehl wird die Ausgabe auf dem Bildschirm angezeigt. Warten Sie dann mit dem Befehl, bis die Benutzereingabe fortgesetzt wird. Beispiel sed -i <pattern> Ersetzen Sie es durch sed <pattern> read -p "Press any key..." sed -i <pattern>

Es wird auch immer empfohlen, Sicherungspunkte in Ihrem System zu behalten, damit im Notfall Daten wiederhergestellt werden können.

Rohit Raj Mishra
quelle
3

Haben set –noder Anfügen nzu Ihrem bestehenden set –vBefehl. Da dies jedoch dazu führt, dass die Shell keine Befehle auswertet, auch nicht ifoder while, erhalten Sie möglicherweise keine allzu gute Sicht auf den Betriebsablauf.

Scott
quelle
Vielen Dank. Dies ist ein guter Anfang, aber wie Sie bereits erwähnt haben, kann ich den tatsächlichen Ablauf nicht sehen. In meinem realen Skript habe ich eine Schleife über eine Liste von Remote-Hosts.
Ysap
3

Hier ist ein kleines Konstrukt, das ich gerne benutze:

#!/bin/sh

verbose=false
really=true

# parse arguments
while [ $# -ge 1 ]
do
  case "$1" in
    -v) verbose=true ;;
    -n) really=false; verbose=true ;;
  esac
  shift
done

doCmd() {
  if $verbose; then echo "$@"; fi
  if $really; then "$@"; fi
}

doCmd make foo
doCmd rm -rf /tmp/installdir
doCmd mkdir /tmp/installdir
doCmd whatever
doCmd blah blah
Edward Falk
quelle
2

Wenn Sie verhindern möchten, dass Änderungen vorgenommen werden, können Sie das Skript als Benutzer ohne Berechtigungen ausführen:

sudo -u nobody ./scr.sh

In Ihrem Beispiel wird dies FN="filename"wie gewohnt ausgeführt. Während der Befehl rm -f ${FN}auch technisch ausgeführt wird, geschieht nichts, wenn niemand über die erforderlichen Berechtigungen zum Löschen der Datei verfügt.

Dies führt natürlich zu Problemen mit dem bedingten Workflow. Die Tatsache, dass der rmBefehl fehlgeschlagen ist, kann sich auf weitere Teile des Skripts auswirken.

Dennis
quelle
Dinge, die wir nie verstehen werden: Warum root als niemand laufen muss ...
mjohnsonengr
Der Benutzer "Nobody" ist nicht speziell für das Betriebssystem, aber Sie können einen Eintrag in die sudoers-Datei einfügen, der passwortloses sudo für "Nobody" ermöglicht.
Dennis
Ich weiß nicht. Scheint, als würde das Skript aufgrund eines Fehlers oder eines anderen beendet, bevor es fertig ist.
Edward Falk