delmic
Anmeldungsdatum: 12. März 2010
Beiträge: 116
Wohnort: Grafenrheinfeld
|
Hallo, gibt es eine einfache Möglichkeit den Löschbefehl zu negieren?
Ich möchte alle Dateien eines Verzeichnisses löschen außer denen die ein v vor dem . enthalten also einfach rm *v.* nur sollen genau diese nicht gelöscht werden. 😀
Vielen Dank
Moderiert von Taomon: Schieb nach Shellzeugs.
|
ChickenLipsRfun2eat
Anmeldungsdatum: 6. Dezember 2009
Beiträge: 12067
|
Ich würde das über find lösen. Beispiel:
| find . -type f -not -iname "*v.*" -ls
|
|
apt-ghetto
Anmeldungsdatum: 3. Juni 2014
Beiträge: 2943
|
Mit der Bash könnte dies eventuell so gehen
| # extglob aktivieren
shopt -s extglob
# Falls es funktioniert, "echo" entfernen
echo rm !(*v.*)
# und extglob wieder deaktivieren
shopt -u extglob
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11260
Wohnort: München
|
Reicht da nicht sowas (das -i kann man natürlich weglassen, wenn man sich davon überzeugt hat, dass der Befehl das tut was man will)?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13204
|
seahawk1986 schrieb: Reicht da nicht sowas (das -i kann man natürlich weglassen, wenn man sich davon überzeugt hat, dass der Befehl das tut was man will)?
Nein, denn das findet die falschen Dateien: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | $ for pre in '' a; do for post in '' b; do for f in v v. .v .; do touch "$pre$f$post"; done
> done; done
$ ls -1
a
a.
a.b
av
a.v
av.
avb
a.vb
av.b
b.
v
v.
vb
v.b
$ echo rm -i *[!v].*
rm -i a. a.b a.v a.vb b.
|
Es werden einige Dateien nicht gelöscht, die aber gelöscht werden sollten (z.B. "a", "av"). Das liegt daran, dass Dein Suchmuster zwingend einen Punkt erfordert - aber das ist etwas anderes als "alle Dateien, die nicht 'v.' enthalten". Die anderen beiden Lösungen: | $ find . -type f \! -iname '*v.*' -exec basename {} \; | paste -sd ' '
av a.b a .b .vb .v vb .c b. avb a.v a. a.vb v
$ echo !(*v.*)
a a. a.b av a.v avb a.vb b. v vb
|
Das richtige Ergebnis liefert nur die Lösung mit find : 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | $ diff -U3 <(find . -type f \! -iname '*v.*' -exec basename {} \; | sort) <(printf '%s\n' !(*v.*) | sort)
--- /dev/fd/63 2017-12-06 12:52:46.706136316 +0100
+++ /dev/fd/62 2017-12-06 12:52:46.710136400 +0100
@@ -5,10 +5,6 @@
a.v
avb
a.vb
-.b
b.
-.c
v
-.v
vb
-.vb
|
So testet man das strukturiert. ☺
|
delmic
(Themenstarter)
Anmeldungsdatum: 12. März 2010
Beiträge: 116
Wohnort: Grafenrheinfeld
|
Vielen Dank für die Vorschläge.
Passt schon für mich.
Ich habe das ! bei rm nicht gefunden wusste eben auch nicht ob man das einfach so negieren kann.
Einfachste Lösung.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13204
|
delmic schrieb: Vielen Dank für die Vorschläge.
Passt schon für mich.
Da implementiert allerdings nicht die Regel, die Du im ersten Posting gegeben hast.
Ich habe das ! bei rm nicht gefunden wusste eben auch nicht ob man das einfach so negieren kann.
Das hat auch nix mit rm zu tun. Das Globbing (Filename Expansion) macht die Shell.
Einfachste Lösung.
Naja, ich finde dieses auch nicht sehr einfach: | find -type f \! -iname '*v.*' -delete
|
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Um das Ganze noch mal systematisch zu betrachten: es geht hier um die Filename Expansion der Shell, ganz unabhängig von rm oder welchem Befehl auch immer. Gucken wir also mal, wie das Muster für die Shell aussehen müsste:
Das was Du behalten möchtest, sind die Dateien nach dem Muster *v.* - ok. Nun kann die Shell das Muster genau umkehren (→ der letzte Eintrag unter Pattern Matching): | n=0
for f in !(*v.*) ; do
echo "$f"
((n++))
done
echo $n
|
sollte Dir die korrekte Liste auswerfen. (→ die Zahl der Treffer dieser beiden Muster sollte die Gesamtzahl der Dateien ergeben - sonst ist da was faul !) Wahrscheinlich wird man das gewünschte Muster auch noch anders bauen können, aber bestimmt nicht einfacher.
LG, track
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13204
|
track schrieb:
Gucken wir also mal, wie das Muster für die Shell aussehen müsste:
Das was Du behalten möchtest, sind die Dateien nach dem Muster *v.* - ok. Nun kann die Shell das Muster genau umkehren (→ der letzte Eintrag unter Pattern Matching): | n=0
for f in !(*v.*) ; do
echo "$f"
((n++))
done
echo $n
|
sollte Dir die korrekte Liste auswerfen.
Tut sie aber nicht (s.o.). Oder entdeckst Du einen Fehler in meinem Test?
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Hm ... wenn ich jetzt mal meinen Vorschlag, und Dein Globbing-Muster auf mein Home-Verzeichnis loslasse, bekommen ich folgendes: track@track:~$ n=0;
track@track:~$ for f in *v.* ; do ((n++)); done # Dateien mit "v." drin
track@track:~$ echo $n
5
track@track:~$ n=0
track@track:~$ for f in !(*v.*) ; do ((n++)); done # mein Muster:
track@track:~$ echo $n
754
track@track:~$ n=0;
track@track:~$ for f in * ; do ((n++)); done # bezieht sich auf alle Dateinamen
track@track:~$ echo $n # (einschliesslich solcher, wo gar kein Punkt drin vorkommt)
759
track@track:~$ n=0;
track@track:~$ for f in *[!v].* ; do ((n++)); done # Dein Muster:
track@track:~$ echo $n
622
track@track:~$ n=0;
track@track:~$ for f in *.* ; do ((n++)); done # bezieht sich offensichtlich nur auf solche mit einem Punkt drin
track@track:~$ echo $n
627 Zum Vergleich mal Dein find -Befehl: track@track:~$ find -maxdepth 1 -type f -iname '*v.*' -printf "x\n" | wc
5 5 10
track@track:~$ find -maxdepth 1 -type f \! -iname '*v.*' -printf "x\n" | wc
764 764 1528
track@track:~$ find -maxdepth 1 -type f -printf "x\n" | wc
769 769 1538
track@track:~$ ls -b1 | wc
759 843 13004 - bezieht sich offensichtlich auch auf alle Dateinamen (einschliesslich der, die gar keinen Punkt enthalten), findet aber noch ± 10 Treffer mehr - allerdings habe ich keine Ahnung, woher die kommen könnten.** Mich beruhigt allerdings, dass ls genau so viele Treffer anzeigt wie mein Muster ... So far ...
LG, track ** Edit: So - jetzt habe ich die Listen mal geloggt und mit meld verglichen: Der Unterschied ist der, dass in Deiner find -Liste auch alle versteckten Dateien mit drin stehen (aber keine Verzeichnisnamen !), während die Filename Expansion auch alle Verzeichnisnamen mit liefert (aber keine versteckten Dateien !). Tja, Pest oder Cholera: man muss wohl in beiden Fällen mal gucken, was man braucht, bzw. wie man die unerwünschten Irrläufer ausblendet ... 😉 😀
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13204
|
Ich würde ja lieber find verwenden, weil das einfach mehr Kontrolle erlaubt - selbst, wenn man die Tiefe mit "-maxdepth 1" begrenzt. Das Glob-Muster unterscheidet ja auch nicht zwischen Dateien und Ordnern, wie Du richtig bemerkt hast. Also, das ist mein Favorit für die ursprüngliche Aufgabenstellung: | find -type f \! -iname '*v.*' -delete
|
|
ChickenLipsRfun2eat
Anmeldungsdatum: 6. Dezember 2009
Beiträge: 12067
|
Zudem bring iname case-insensitivity mit sich, die for f in *v.* -Methode müsste also mit [V|v] ergänzt werden, oder die find -Methode mit name statt iname aufgerufen werden, oder?
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 13204
|
ChickenLipsRfun2eat schrieb: Zudem bring iname case-insensitivity mit sich, die for f in *v.* -Methode müsste also mit [V|v]
Ohne das Pipe-Symbol: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | $ ls -1
|a
v.
V.
va
Va
$ printf '%s\n' [vV]*
v.
V.
va
Va
$ printf '%s\n' [v\|V]*
|a
v.
V.
va
Va
|
ergänzt werden, oder die find -Methode mit name statt iname aufgerufen werden, oder?
Genau.
|