[megoldva] find furcsaság

Valamit nem értek. Persze lehet, hogy csak nem veszek észre valami triviálisat, ha erről lenne szó, írjátok meg! Köszi.

Feladat: ki akartam válogatni a mentéseimből a videókat, amik egy adott dátum után készültek.

Több iteráción keresztül eljutottam oda, hogy azonosítottam azokat a fájl kiterjesztéseket, amik nem képek, nem egyebek, hanem vagy videó, vagy nem ismerem és akár videó is lehet.

Megszámoltam, hány ilyen van (1):

$ find -L /mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 | wc -l
834

OK, gondoltam legyűjtöm ezeket egy helyre, hogy ne kelljen mindig a finddal vacakolni (2):

$ find -L /mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 -print -exec ln -s '{}' . \;

A könyvtárban viszont ennél jóval kevesebb fájl lett:

$ ls -1 | wc -l
348

Na, gondoltam, bizonyára voltak duplikációk, nem tudta ugyanazt a symlinket használni többször azonos nevű fájlokhoz, stb.
Jó, gondoltam, lementem a fájl listát, basename | sort | uniq és meglátjuk, hogy jó eredmény jön-e ki, vagy van olyan, ami valami más miatt maradt le. (3)

$ find -L /mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 -print > filelistln-s.txt
$ wc -l filelistln-s.txt
348 filelistln-s.txt

Izé. Itt elakadtam.
Ha jól látom, a két parancs (1), ami 834-et ad, és (2) meg (3), ami 348-at ad, ugyanazokat a válogatási paramétereket adja a findnak, csak a -print az eltérés.

Nézzük, mit mond a man find:

If no expression is given, the expression -print is used
...
-print True; print the full file name on the standard output, followed by a newline.

Szóval elméletileg a -print megadása az utasítás végén az égvilágon semmit nem változtat, ha nem adom meg, akkor is ugyanúgy végrehajtja.

(a problémámat a két különböző fájl lista összehasonlításával tudom egyébként kezelni, szóval ez nem akadályoz, csak elgondolkoztatott).

Viszont tovább nézve a fájlok különbségét, feltűnt, hogy a 834 darabosban számos olyan fájl van, ami a megadott dátum előtti. A 348-asban nem találtam egyet se (szemmel átfutva).
Nem tudom még, hogy ez-e az egyetlen különbség, de ez megint felvet egy kérdést:

Ha a két esetben a find-nak átadott paraméterlista ugyanaz, akkor miért lehet eltérés az eredményben?
Azt írja a man, hogy operátorok (pl. -o és -a) kapcsolják össze a kifejezés elemeit, és ha hiányzik az operátor, akkor logikai és kapcsolatot feltételez.
Ez alapján a -newerXY elem és kapcsolatban van a többivel, szóval nem értem, hogy találhatott a find olyasmit, ami korábbi.
És máskor meg ügyesen kihagyja ezeket.

Hozzászólások

for i in $(find -L * -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15); do ln -s $i; done

$ find -L /mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 -print > filelistln-s.txt

helyette:
$ rm filelistln-s.txt
$ touch filelistln-s.txt
$ find -L /mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 >> filelistln-s.txt

üdv: pomm

A 852-es kídlap telepötúsa sikeresen befejezádétt

Próbáld ki, mi történik, ha kiteszed a zárójeleket?


find -L /mnt/nas/backup/Pictures/ \
  \( -iname '*.3gp' -o -iname '*.amr' -o -iname '*.m4a' -o -iname '*.mov' -o -iname '*.avi' -o -iname '*.mp4' \) \
  -type f -newermt 2013-11-15

Érdekes. Úgy tűnik, így működik. Köszönöm, jó ötlet, tegnap éjjel nem jutott eszembe ebben az irányban keresni a hibát.


gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ \( -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 \) -type f -newermt 2013-11-15 -print | wc -l
7
gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ \( -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 \) -type f -newermt 2013-11-15 | wc -l
7

Ezek alapján arra gondolok, hogy nem simán balról jobbra dolgozza fel a kifejezés részeit, ahogy én azt a man alapján gondoltam, hanem a boolean AND magasabb precedenciájú mint a boolean OR.

Lássuk, megtalálom-e a manuálban (ez a hátránya az ilyen hosszú man oldalaknak. Az ember rákeres erre-arra, de nem olvas mindent végig)

GNU find ... [evaluates the]
expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known (the left hand side is false for and operations, true for or), at which point find moves on to the next file name
OPERATORS
Listed in order of decreasing precedence:
( expr )
...
expr1 -a expr2
expr1 -o expr2

Please note that -a when specified implicitly (for example by two tests appearing without an explicit operator between them) or explicitly has higher precedence than -o. This means that find . -name afile -o -name bfile -print will never print afile.

Egyfelől igen, megvan a rész, amit nem olvastam el éjjel.

És az utolsó példa arra is rávilágít, miért változtatta meg a -print az eredményt.

Ezek szerint ha explicit kiírtam a -print vagy -exec akciót, akkor csak a friss mp4-ekre illeszkedett, ezek nélkül meg a friss mp4-ekre és az összes többi kiterjesztésre dátum ellenőrzés nélkül.

Amúgy a képek dátum szerinti rendezésére ezt is használhatod (ha már egyszer vki megcsinálta):
https://github.com/mb243/exifsort
A videóknál a mediainfo-t érdemes használni a metaadatokhoz, vélhetően pontosabb lesz, mint az egyéb dátumok....
Akár a képes script is átírható videóra :)
üdv: pomm

A 852-es kídlap telepötúsa sikeresen befejezádétt

Igazából nem a sorbarendezés volt a lényeg.

Minden* videót ki akartam gyűjteni, ami egy konkrét dátum után készült.

* de igazából óriási pontosság nem kell, ha belekerül pár videó ami korábbi, az nem gond, és az sem, ha néhány nincs benne a listámban.
Az viszont zavar, ha a találatok több, mint fele teljesen rossz (túl korai)

Idézőjel-idézőjel-idézőjel!

$  find /home/storage/kepek/ -iname *.jpg | wc -l                                                                                         
0  
$  find /home/storage/kepek/ -iname "*.jpg" | wc -l                                                                                        
5553

Megoldás:

$  ls                                                                                                                                     
x.jpg

Szóval elméletileg a -print megadása az utasítás végén az égvilágon semmit nem változtat, ha nem adom meg, akkor is ugyanúgy végrehajtja.

A man azt írja, ha nincs semmi "akció" megadva, akkor a -print fut le (If no expression is given).

Persze ezek nem feltétlen adnak választ a kérdéseidre, viszont érdemes szem előtt tartani.

Igen, az idézőjel okozhatott volna gondot, de újra futtatva nem adott különböző eredményt.

Közben készítettem egy kisebb halmazt tesztelni (a nagyobb halmazon minden futás 2 perc felett volt):


ee@hawk:~/Videok_teszt/findtest$ ls -lA
total 4
drwxr-xr-x 3 gee gee 4096 Oct 22 23:38 mnt/
gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 | wc -l
36
gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ -iname *.3gp -o -iname *.amr -o -iname *.m4a -o -iname *.mov -o -iname *.avi -o -iname *.mp4 -type f -newermt 2013-11-15 -print | wc -l
7
gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ -type f -newermt 2013-11-15 -print | wc -l17
gee@hawk:~/Videok_teszt/findtest$ find -L mnt/nas/backup/Pictures/ -type f -newermt 2013-11-15 | wc -l
17

Ahogy az első két find utasításból látható, az üres könyvtáron is ugyanaz a jelenség adódik.

Viszont érdekes módon, ha nem próbálok névre szűrni, akkor azonos az eredmény.

Úgy már próbáltad, ahogy NevemTeve javasolta?
Egy hevenyészett teszttel én is hasonló eredményt produkálok:

$ mkdir -p a/a{b,c,d,e}
$ touch a/a{b,c,d,e}/f0{1,2,3,4,5}.avi
$ touch a/a{b,c,d,e}/f0{1,2,3,4,5}.jpg
$ touch a/a{b,c,d,e}/f0{1,2,3,4,5}.png

find-parancsok:


$ find a/ -print -iname "*.png" -o -iname "*.jpg" -type f | wc -l
65
$ find a/ -iname "*.png" -o -iname "*.jpg" -type f -print | wc -l
20

A két lekérdezésben annyi a különbség, hogy a -print az elején vagy a végén van-e. A trükk az, hogy a -print nem utasítás, hogy mit csináljon, hanem "teszt" ("expression", "primaries"), ami mindig igazat ad vissza.
Az első lényegében ("print" és "png") vagy ("jpg" és "file") módon értelmezendő.
A második ("png") vagy ("jpg" és "file" és "print") (pl. egy foo.png nevű könyvtárat(!) is listáz).

Bár bevallom, az alábbi eredmények nemigen tiszták számomra:

$  find a/ \( -iname "*.jpg" -o -iname "*.png" \) -type f -print | wc -l
40
$  find a/ -print \( -iname "*.jpg" -o -iname "*.png" \) -type f | wc -l
65

Hm. Lehet, hogy van benne valami:

$ find a/ -print -o -iname "*.xyz" | wc -l
65
$ find a/ -print -iname "*.xyz" | wc -l
65

És ha a végén van?

$ find a/ -iname "*.xyz" -o -print | wc -l
65
$ find a/ -iname "*.xyz" -print | wc -l
0

Nemigen értem a logikát, ami lenne az egész mögött. Első közelítésre azt mondanám, hogy az és művelet nem kommutatív. Vagy esetleg az a mondás, hogy ha az -iname hamist ad, akkor a -print értékétől függetlenül úgyis hamis a végeredmény?

A logikai műveleteknél számít a sorrend (ha nem, az tervezési hiba):


true  AND valami => valami
false AND valami => false ('valami'-t nem számoljuk ki / végezzük el)
true  OR  valami => true  ('valami'-t nem számoljuk ki / végezzük el)
false OR  valami => valami

ha van/lenne IMP (implies), akkor:


true  IMP valami => valami
false IMP valami => true  ('valami'-t nem számoljuk ki / végezzük el)

Akkor ez miért van így (true AND false)?

$ find a/ -print -iname "*.xyz" | wc -l
65

Vagy a -print-re nem úgy kell gondolni, mint egy true értékre, hanem inkább úgy, mint egy függvényre, ami mindig true-t ad vissza és (amikor a kiértékelésére kerül a sor) mellékesen kiír egy rakat dolgot?

Talán az utóbbi verziót támasztja alá:

$ find a/ -print | wc -l
65
$ find a/ -print -print | wc -l
130