Neue Zeilen an mehrere Dateien anhängen

650
Andrew Davis

Ich versuche, mit dem folgenden Befehl neue Zeilen an mehrere Dateien anzufügen:

find -name *.ovpn -exec sh echo "line to append" >> {} \; 

Bevor ich dies tat, habe ich einen anderen Befehl ausgeführt, um sicherzustellen, dass er wie erwartet funktioniert:

find -name *.ovpn -exec sh echo "hello" \; 

Alles, was Sie tun müssen, ist "sh: 0: Echo kann nicht geöffnet werden" für jede gefundene Datei.

1

1 Antwort auf die Frage

2
Kamil Maciorowski

Es gibt wenige Probleme.

>>in Ihrem ersten Befehl wird von Ihrer aktuellen Shell als Umleitung zu einer Datei interpretiert, deren Name buchstäblich benannt ist {}, sofern diese nicht in Anführungszeichen gesetzt ist.

*.ovpnkann durch Shell-Globbing erweitert werden, bevor es ausgeführt wird find. Dies ist der Fall, wenn sich im aktuellen Verzeichnis mindestens ein Objekt befindet, das dem Muster entspricht. Sie möchten dies zitieren. Vergleichen Sie diese Frage .

Sie bekommen, Can't open echoweil Sie tatsächlich sagen sh, dass Sie sich öffnen sollen echo. Um einen Befehl auszuführen, benötigen Sie sh -c.

findohne Angabe des Pfads ist nicht portierbar (vergleiche diese Frage ). Während Sie damit umgehen können, erwähne ich das Problem, um die Antwort für andere Benutzer nützlicher zu machen.

Dies ist die verbesserte Version Ihres ersten Befehls, der irgendwie funktioniert (führen Sie ihn nicht aus, lesen Sie weiter):

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "{}"' \; 

Beachten Sie, dass ich in {}Anführungszeichen ein Zitat setzen musste . Diese Anführungszeichen werden von "gesehen" shund lassen Dateinamen mit Leerzeichen usw. als Umleitungsziele fungieren. Ohne Anführungszeichen können Sie mit like enden, echo "line to append" >> foo bar.ovpnwas äquivalent ist echo "line to append" bar.ovpn >> foo. Zitat macht es echo "line to append" >> "foo bar.ovpn"stattdessen.

Leider werden Dateinamen mit "dieser Syntax gebrochen.

Der richtige Weg zu passieren, {}um shnicht in der Befehlsfolge enthält aber ihren Inhalt als separates Argument zu übergeben:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$0"' {} \; 

$0innerhalb der Befehlsfolge an das unsere Premieren Argument erweitert shwird nach -c '…'. Jetzt auch "in Dateiname wird die Syntax nicht gebrochen.

Normalerweise (wie in einem Skript), um auf das erste Argument zu verweisen, das Sie verwenden $1. Dies ist der Grund, warum einige Benutzer lieber ein Dummy-Argument verwenden würden $0, wie dieses:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' dummy {} \; 

Wenn es ein Skript $0wäre, würde es zu seinem Namen erweitert. Deshalb ist es nicht ungewöhnlich, dass dies dummytatsächlich der Fall ist sh(oder bash, wenn jemand anruft bash -c …usw.):

find . -name '*.ovpn' -exec sh -c 'echo "line to append" >> "$1"' sh {} \; 

Aber warte! findruft shfür jede einzelne Datei ein separates auf. Ich erwarte nicht, dass Sie Tausende .ovpnDateien haben, aber im Allgemeinen möchten Sie viele Dateien verarbeiten, ohne unnötige Prozesse zu erzeugen. Wir können den Ansatz optimieren, tee -aindem in einem einzigen Prozess in mehrere Dateien geschrieben werden kann:

find . -name '*.ovpn' -exec sh -c 'echo "line to append" | tee -a "$@" >/dev/null' sh {} + 

Beachten Sie {} +, dass dies mehrere Pfade gleichzeitig durchläuft. Innerhalb des Befehls, den sh -cwir ausführen "$@", rufen wir sie mit ab, was zu erweitert wird "$1" "$2" "$3" …. In diesem Fall ist ein Dummy-Argument (nicht verwendet) $0ein Muss.

Im Allgemeinen gibt es auch dieses Thema: Warum ist printfbesser als echo? In diesem Fall verwenden Sie jedoch echokeine Optionen, und die Zeichenfolge ist statisch. Sie sollte also in Ordnung sein.