Forum: PC-Programmierung for f in Teilmengen von Dateien


von shelly (Gast)


Lesenswert?

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"

von Jann S. (pycta)


Lesenswert?

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.
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

von shelly (Gast)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
von shelly (Gast)


Lesenswert?

Ah ja die Gänsefüsschen - ich probiere mal

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von klatschnass (Gast)


Lesenswert?

Und die Leerzeichen in der if Bedingung und vor und hinter den eckigen 
Klammern sind wichtig!

von shelly (Gast)


Lesenswert?

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..

von shelly (Gast)


Lesenswert?

Frank M. schrieb:
> Vielleicht baust Du noch zu Anfang ein
>
>    set -e

ist jetzt drin

von shelly (Gast)


Lesenswert?

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?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
von shelly (Gast)


Lesenswert?

Jetzt klappt alles! Danke!

von Rolf M. (rmagnus)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
von Rolf M. (rmagnus)


Lesenswert?

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
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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 
;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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
von Rolf M. (rmagnus)


Lesenswert?

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".

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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

von foobar (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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
von tictactoe (Gast)


Lesenswert?

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.

von Minimalist (Gast)


Lesenswert?

Versuch mal
rsync -rvcm --compare-dest=../old/ new/ difference/

Ggf noch ein zweites mal mit old und New vertauscht.

von foo (Gast)


Lesenswert?

find /foo | grep -v bar|bob

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
Noch kein Account? Hier anmelden.