Hi, ich hab hier schon praktische Scripte gesehen für Linux! Ich möchte gerade eins entwickeln, das 2 Verzeichnisse anhand der Dateinamen vergleicht, und die Dateien, die nicht gleich sind, in ein drittes Verzeichnis kopiert. Wie geht das? Ich hab oft gesehen "for f in... <machwas>" aber ich brauche ja "for f in b and not in a" :-) Ich hoffe Ihr versteht, was ich meine... also irgendwie das Gegenteil von "in"
Pseudocodemäßig ausgedrückt sollte etwas in der Art von
1 | for f in a do |
2 | if[f not in b] then |
3 | //Kopieren |
4 | fi |
5 | done |
tun.
:
Bearbeitet durch User
Beitrag #5806791 wurde vom Autor gelöscht.
shelly schrieb: > Ich möchte gerade eins entwickeln, das 2 Verzeichnisse anhand der > Dateinamen vergleicht, und die Dateien, die nicht gleich sind, in ein > drittes Verzeichnis kopiert. So ist das missverständlich. Wähle bitte die richtige Option: [ ] die Dateien, die vom Dateinamen her nicht gleich sind [ ] die Dateien, die gleich heißen, aber vom Datei-Inhalt her nicht gleich sind
erste option :-) Ich hab mal so probiert, er mag das "fi" nicht
1 | #!/bin/bash
|
2 | a=verz1/* |
3 | b=verz2/* |
4 | c=verz3 |
5 | for f in a; do |
6 | if[f not in b]; |
7 | cp "$f" "$c"/"$f" |
8 | fi
|
9 | done
|
1 | #! /bin/sh
|
2 | a="/tmp/dir1" # change here |
3 | b="/tmp/dir2" # change here |
4 | c="/tmp/dir3" # change here |
5 | |
6 | cd "$a" |
7 | for j in * |
8 | do
|
9 | if [ ! -f "$b/$j" ] |
10 | then
|
11 | cp "$j" "$c/$j" |
12 | fi
|
13 | done
|
EDIT: Gänsefüßchen eingebaut für Dateien/Ordner mit Leerzeichen im Namen.
:
Bearbeitet durch Moderator
shelly schrieb: > Ah ja die Gänsefüsschen - ich probiere mal Vielleicht baust Du noch zu Anfang ein set -e ein. Dann wird das Script abgebrochen, wenn ein Fehler auftritt. Beispiel: Wenn das Verzeichnis $a nicht existiert und der cd-Befehl fehlschlägt, ist es höchst sinnvoll, abzubrechen und nicht weiterzumachen.
Und die Leerzeichen in der if Bedingung und vor und hinter den eckigen Klammern sind wichtig!
Es klappt noch ncith ganz, weil ich mit relativen Pfaden arbeiten will: #! /bin/sh a="./verz1" b="./verz2" c="./verz3" cd "$a" for j in * do if [ ! -f "$b/$j" ] then cp "$j" "$c/$j" fi done beim "cp ist er dann schon eins zu tief..
Ich dachte er löst das "./" bei der Zuweisung auf in den kompletten Pfad, tut er aber wohl nicht - oder gibt es da eine einfache Methode?
shelly schrieb: > Es klappt noch ncith ganz, weil ich mit relativen Pfaden arbeiten will: Dann halt so:
1 | #! /bin/sh
|
2 | a="dir1" # change here |
3 | b="dir2" # change here |
4 | c="dir3" # change here |
5 | |
6 | set -e |
7 | pwd=`pwd` # das sind backticks! |
8 | |
9 | a="$pwd/$a" |
10 | b="$pwd/$b" |
11 | c="$pwd/$c" |
12 | |
13 | cd "$a" |
14 | for j in * |
15 | do
|
16 | if [ ! -f "$b/$j" ] |
17 | then
|
18 | cp "$j" "$c/$j" |
19 | fi
|
20 | done
|
Wenn alle 3 Ordner relativ zum aktuellen sind und lediglich die Tiefe 1 haben, dann gehts auch so:
1 | #! /bin/sh
|
2 | a="dir1" # change here |
3 | b="dir2" # change here |
4 | c="dir3" # change here |
5 | |
6 | set -e |
7 | |
8 | cd "$a" |
9 | for j in * |
10 | do
|
11 | if [ ! -f "../$b/$j" ] |
12 | then
|
13 | cp "$j" "../$c/$j" |
14 | fi
|
15 | done
|
Warum mache ich den cd? Damit ich in $j platte Dateinamen habe und keinen Ordnernamen. Sonst kann man darauf keinen f-Test in einem völlig anderen Ordner machen bzw. auch der cp würde fehlschlagen. Wenn Du unbedingt auf den cd-Befehl verzichten willst, dann muss man sich umständlich mittels basename den Dateinamen ohne Pfad holen. Das erspare ich mir durch den cd-Befehl.
:
Bearbeitet durch Moderator
klatschnass schrieb: > Und die Leerzeichen in der if Bedingung und vor und hinter den eckigen > Klammern sind wichtig! Ja, denn [ ist ein Programm, das ausgeführt wird, und es erwartet als letzten Kommandozeilenparameter das ]. shelly schrieb: > Ich dachte er löst das "./" bei der Zuweisung auf in den kompletten > Pfad, tut er aber wohl nicht Nein, . ist ein relativer Verweis auf das aktuelle Verzeichnis. Warum sollte da was aufgelöst werden? Frank M. schrieb: > pwd=`pwd` # das sind backticks! > > a="$pwd/$a" > b="$pwd/$b" > c="$pwd/$c" Statt sich ein eigenes $pwd zu definieren, kann man auch einfach die schon vordefinierte Variable $PWD verwenden.
Rolf M. schrieb: > Statt sich ein eigenes $pwd zu definieren, kann man auch einfach die > schon vordefinierte Variable $PWD verwenden. Kann man, aber $PWD ist ein bashism. bashisms vermeide ich grundsätzlich - besonders dann, wenn ich jemandem etwas allgemeines erklären will.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Kann man, aber das ist bashism. Nö, ist POSIX-Standard. Siehe http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03
:
Bearbeitet durch User
Rolf M. schrieb: > Nö, ist POSIX-Standard. Danke für den Hinweis. Ich kann mich aber noch an Zeiten erinnern, wo das nicht der Fall war ;-)
Apropos, ich sehe gerade, dass auch die Command Substitution per $(command) es mittlerwiele in den POSIX-Standard geschafft hat. Nett :-) Ebenso die Arithmetic Expansion per $((expression)). Sehr löblich. Damit kann man nun das ewig lästige i=`expr $i + 1` zum Inkrementieren einer Variablen etwas eleganter schreiben.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Apropos, ich sehe gerade, dass auch die Command Substitution per > $(command) es mittlerwiele in den POSIX-Standard geschafft hat. Nett :-) Soweit ich weiß, sind die Backticks schon lange "deprecated".
Rolf M. schrieb: > Soweit ich weiß, sind die Backticks schon lange "deprecated". Davon steht in dem POSIX-Dokument aber nichts. Hier werden beide Variaten gleich behandelt. Natürlich ist die $()-Variante besser zu schachteln, wenn man es komplizierter mag. Interessant zum Thema: https://unix.stackexchange.com/questions/126927/have-backticks-i-e-cmd-in-sh-shells-been-deprecated
1 | #!/bin/sh
|
2 | |
3 | copyexcl() { # source exclude destination |
4 | for i in "$1"/*; do |
5 | test -e "$2/${i##*/}" || cp "$i" "$3" |
6 | done
|
7 | }
|
8 | |
9 | copydiff() { # source1 source2 destination |
10 | copyexcl "$1" "$2" "$3" |
11 | copyexcl "$2" "$1" "$3" |
12 | }
|
13 | |
14 | copydiff ../dir1 /foo/dir2 new |
Frank M. schrieb: > Interessant zum Thema: > > https://unix.stackexchange.com/questions/126927/have-backticks-i-e-cmd-in-sh-shells-been-deprecated Den hatte ich auch gefunden. Dort findet man den Link zur POSIX-Rationale für $() http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_06_03 wo es heißt: "Because of these inconsistent behaviors, the backquoted variety of command substitution is not recommended for new applications that nest command substitutions or attempt to embed complex scripts."
:
Bearbeitet durch User
Noch ein Vorschlag, braucht aber die Bash, GNU cp und xargs:
1 | #!/bin/bash
|
2 | a="dir1" # change here |
3 | b="dir2" # change here |
4 | c="dir3" # change here |
5 | |
6 | |
7 | comm -23 <(cd "$a" && ls -1) <(cd "$b" && ls -1) | |
8 | sed -e "s,^,$a/," | |
9 | xargs --no-run-if-empty cp --target-directory="$c" |
Zur Erklärung: Vergleiche zwei sortierte Listen, schreibe die Werte, die nur in der linken Liste vorkommen. Füge am Anfang jeder dieser Zeilen noch den Quellpfad $a ein. Kopiere alles genannte in das Zielverzeichnis $c.
Versuch mal rsync -rvcm --compare-dest=../old/ new/ difference/ Ggf noch ein zweites mal mit old und New vertauscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.