Im Moment verwende ich beispielsweise Folgendes, um einige Dateien zu ändern, deren Unix-Pfade ich in eine Datei geschrieben habe:
cat file.txt | while read in; do chmod 755 "$in"; done
Gibt es einen eleganteren und sichereren Weg?
Dies liegt daran, dass es nicht nur 1 Antwort gibt ...
shell
Befehlszeilenerweiterungxargs
dediziertes Werkzeugwhile read
mit einigen Bemerkungenwhile read -u
Verwendung dedizierter fd
, für die interaktive Verarbeitung (Beispiel)Die OP Anfrage in Bezug auf : Laufen chmod
auf allen Zielen in Datei aufgeführt , xargs
ist das angezeigte Werkzeug. Aber für einige andere Anwendungen, kleine Menge von Dateien, etc ...
Wenn Ihre Datei nicht zu groß ist und alle Dateien einen guten Namen haben (ohne Leerzeichen oder andere Sonderzeichen wie Anführungszeichen), können Sie die shell
Befehlszeilenerweiterung verwenden . Einfach:
chmod 755 $(<file.txt)
Für eine kleine Anzahl von Dateien (Zeilen) ist dieser Befehl der leichtere.
xargs
ist das richtige WerkzeugFür eine größere Anzahl von Dateien oder fast eine beliebige Anzahl von Zeilen in Ihrer Eingabedatei ...
Für viele binutils Tools, wie chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Wenn Sie spezielle Zeichen und / oder viele Zeilen haben file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
Wenn Ihr Befehl genau 1 Mal per Eintrag ausgeführt werden muss:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Dies wird für dieses Beispiel nicht benötigt, da chmod
mehrere Dateien als Argument akzeptiert werden, dies entspricht jedoch dem Titel der Frage.
In einigen speziellen Fällen können Sie sogar den Speicherort des Dateiarguments in Befehlen definieren, die generiert werden von xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
als EingabeVersuche dies:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
Wo Befehl einmal pro Zeile ausgeführt wird .
while read
und Varianten.Wie OP vorschlagen cat file.txt | while read in; do chmod 755 "$in"; done
wird funktionieren, aber es gibt 2 Probleme:
cat |
ist eine nutzlose Gabel , und
| while ... ;done
wird zu einer Unterschale, in der die Umgebung danach verschwindet ;done
.
Das könnte also besser geschrieben werden:
while read in; do chmod 755 "$in"; done < file.txt
Aber,
Sie können gewarnt werden $IFS
und read
Flaggen:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
In einigen Fällen müssen Sie möglicherweise verwenden
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Zur Vermeidung von Problemen mit Stranges-Dateinamen. Und vielleicht, wenn Sie Probleme haben mit UTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
Während Sie STDIN
zum Lesen verwenden file.txt
, kann Ihr Skript nicht interaktiv sein (Sie können es nicht STDIN
mehr verwenden).
while read -u
mit dedizierten fd
.Syntax: while read ...;done <file.txt
leitet STDIN
zu um file.txt
. Das heißt, Sie werden nicht in der Lage sein, mit Prozessen umzugehen, bis sie abgeschlossen sind.
Wenn Sie ein interaktives Tool erstellen möchten, müssen Sie die Verwendung eines STDIN
alternativen Dateideskriptors vermeiden und diesen verwenden .
Konstanten Filedeskriptoren sind: 0
für STDIN , 1
für STDOUT und 2
für STDERR . Sie konnten sie sehen durch:
ls -l /dev/fd/
oder
ls -l /proc/self/fd/
Von dort aus müssen Sie eine nicht verwendete Nummer zwischen 0
und 63
(tatsächlich, abhängig vom sysctl
Superuser-Tool) als Dateideskriptor auswählen :
Für diese Demo werde ich fd verwenden 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Dann könnten Sie read -u 7
diesen Weg benutzen :
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
Zum Schließen fd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Hinweis : Ich habe eine gestrichene Version zugelassen, da diese Syntax nützlich sein kann, wenn viele E / A-Vorgänge mit Parallelen ausgeführt werden:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
xargs
ursprünglich für die Beantwortung dieser Art von Anforderungen erstellt, sorgen einige Funktionen, wie z. B. das Erstellen von Befehlen so lange wie möglich in der aktuellen Umgebung, umchmod
in diesem Fall so wenig wie möglich aufzurufen , und das Reduzieren von Gabeln für Effizienz.while ;do..done <$file
implizieren, dass 1 Gabel für 1 Datei ausgeführt wird.xargs
könnte 1 Gabel für tausend Dateien ausführen ... auf zuverlässige Weise.cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
etwa 50% schneller als die von Ihnen beschriebene Methode.Ja.
Auf diese Weise können Sie einen
cat
Prozess vermeiden .cat
ist für einen solchen Zweck fast immer schlecht. Sie können mehr über die nutzlose Verwendung von Cat lesen .quelle
cat
ist eine gute Idee, aber in diesem Fall ist der angegebene Befehlxargs
chmod
ausführen müssen (dh wirklich einen Befehl für jede Zeile in der Datei ausführen).read -r
liest eine einzelne Zeile aus der Standardeingabe (read
ohne-r
Backslashes zu interpretieren, möchten Sie das nicht)."Wenn Sie einen netten Selektor haben (zum Beispiel alle TXT-Dateien in einem Verzeichnis), können Sie Folgendes tun:
Bash für Schleife
oder eine Variante von dir:
quelle
Wenn Sie wissen, dass die Eingabe kein Leerzeichen enthält:
Wenn die Pfade möglicherweise Leerzeichen enthalten und GNU-xargs vorhanden sind:
quelle
xargs
ist robust. Dieses Tool ist sehr alt und sein Code wird stark überarbeitet . Sein Ziel war es zunächst, Linien in Bezug auf Shell-Einschränkungen (64 kchar / Linie oder so etwas) zu bauen. Jetzt kann dieses Tool mit sehr großen Dateien arbeiten und die Anzahl der Verzweigungen auf den endgültigen Befehl erheblich reduzieren. Siehe meine Antwort und / oderman xargs
.brew install findutils
) installieren und danngxargs
stattdessen GNU xargs mit z. B.gxargs chmod 755 < file.txt
Wenn Sie Ihren Befehl für jede Zeile parallel ausführen möchten, können Sie GNU Parallel verwenden
Jede Zeile Ihrer Datei wird als Argument an das Programm übergeben. Standardmäßig werden
parallel
so viele Threads ausgeführt, wie Ihre CPUs zählen. Sie können es aber mit angeben-j
quelle
Ich sehe, dass Sie bash markiert haben, aber Perl wäre auch ein guter Weg, dies zu tun:
Sie können auch einen regulären Ausdruck anwenden, um sicherzustellen, dass Sie die richtigen Dateien erhalten, z. B. um nur TXT-Dateien zu verarbeiten:
Um eine Vorschau der Ereignisse anzuzeigen, ersetzen Sie einfach die Backticks durch doppelte Anführungszeichen und stellen Sie Folgendes voran
print
:quelle
chmod
Funktionperl -lpe 'chmod 0755, $_' file.txt
- verwenden Sie-l
für die "Auto-Chomp" -FunktionSie können auch AWK verwenden, um mehr Flexibilität beim Umgang mit der Datei zu erhalten
Wenn Ihre Datei ein Feldtrennzeichen hat wie:
Um nur das erste Feld zu erhalten, das Sie tun
Weitere Informationen finden Sie in der GNU-Dokumentation unter https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
quelle
Die Logik gilt für viele andere Ziele. Und wie liest man .sh_history jedes Benutzers aus / home / filesystem? Was ist, wenn es Tausende von ihnen gibt?
Hier ist das Skript https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
quelle
Ich weiß, dass es spät ist, aber immer noch
Wenn Sie zufällig auf eine mit Windows gespeicherte Textdatei mit
\r\n
statt stoßen\n
, werden Sie möglicherweise durch die Ausgabe verwirrt, wenn Ihr Befehl etw nach der Lesezeile als Argument enthält. Entfernen Sie\r
zum Beispiel:quelle