Kopiera filer rekursivt [LÖST]

Här diskuterar vi skal, kommandon och klassiska linuxverktyg.
Användarvisningsbild
brattham
Inlägg: 124
Blev medlem: 24 jul 2008, 22:43
OS: Edubuntu
Utgåva: 22.04 Jammy Jellyfish LTS
Ort: Göteborg

Kopiera filer rekursivt [LÖST]

Inlägg av brattham »

Jag ska skriva ett skript som kopierar ett katalogträd och beroende på vilken typ av fil det handlar om kommer filerna att kopieras till olika destinationer. Katalogträdets struktur skiljer sig åt från gång till gång.

Jag vet inte riktigt någon bra metod för att lösa problemet; det är klurigare än det verkar. Filerna måste kopieras rekursivt (eller åtminstone måste filnamnen hämtas), analyseras för att till sist skickas vidare till rätt destination (målkatalog). Problemet uppkommer då filnamnen som används är av relativt generell karaktär, t ex genom att mellanslag i filnamnen förekommer.

Används t ex "find" (find ./ minKatalog -type f) fås en vacker lista på filerna, men jag vet inte riktigt hur jag ska gå till väga sedan. Jag kan naturligtvis lägga resultatet i en fil, fast i så fall måste jag kunna läsa en rad i taget från filen. Men ska det verkligen vara nödvändigt att behöva lägga resultatet i en fil?

Jag har prövat att lägga resultatet i både en vanlig variabel och i en "array" fast det funkar inte riktigt; då jag hämtar resultatet så delar sig filnamnen vid mellanslagen, vilket naturligtvis inte är så käckt! Oxo om jag satt citationstecken runt om varje filnamn! Varför?

Ex.

Kod: Markera allt

$########################################## Lista filerna med "ls" (två filer i varje katalog)
$ ls -R ./katalog_A/
./katalog_A/:
fil_A1  katalog_B  min fil_A1

./katalog_A/katalog_B:
fil_B  min fil_B
$########################################## Ta bort tidigare innehåll ur "arrayen" a
$ a=( "" )
$ echo ${a[@]}

$ echo ${#a[@]}
1
$########################################## Lista filerna med "find" (fyra st - vilket det ska vara!)
$ find ./katalog_A/ -type f 
./katalog_A/fil_A1
./katalog_A/katalog_B/fil_B
./katalog_A/katalog_B/min fil_B
./katalog_A/min fil_A1
$########################################## Lägg filnamnen i "arrayen" a ( Nu sex filer pga mellanslagen!)
$ a=(`find ./katalog_A/ -type f -exec echo '"{}"' \; `)
$ echo ${#a[@]}
6
$ echo ${a[@]}
"./katalog_A/fil_A1" "./katalog_A/katalog_B/fil_B" "./katalog_A/katalog_B/min fil_B" "./katalog_A/min fil_A1"
$########################################## Lista elementen i "arrayen" a för att tydliggöra det hela
$ echo -e "${a[0]} \n ${a[1]} \n ${a[2]} \n ${a[3]} \n ${a[4]} \n ${a[5]}" 
"./katalog_A/fil_A1" 
 "./katalog_A/katalog_B/fil_B" 
 "./katalog_A/katalog_B/min 
 fil_B" 
 "./katalog_A/min 
 fil_A1"
$########################################## Lägg märke till att citationstecknen delar upp sig på var sin sida av de "falska" filnamnen ovan
Jag har också försökt använda mig av "find" i kombination med "-print0", för att sedan t ex "pipe:a" det via "xargs -0" till något lämplig kommando, fast har inte lyckats komma så mycket längre där heller...

Är det någon som har någon idé hur jag ska gå vidare? Om jag t ex måste spara filnamnen i en fil, hur hämtar jag dem smidigast då (rad för rad); finns det något bättre sätt?

/Mvh Per
Senast redigerad av 2 brattham, redigerad totalt 23 gång.
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Kopiera filer rekursivt

Inlägg av Konservburk »

Knepet är att inte mellanlagra filnamnen, varken i en fil eller i variabler, vare sig det är array-variabler eller inte. Det finns två sätt som fungerar bra. Antingen skickar du resultatet via en pipe till kommandot xargs. Men då måste du se till att allting verkligen separeras med nolltecken genom pipen. Det blir ungefär så här:

Kod: Markera allt

find -type f -print0 | xargs -0r cp -t målkatalog
Alternativet som jag själv föredrar är att använda -exec direkt direkt med find så här:

Kod: Markera allt

find -type f -exec cp -t målkatalog {} +
Båda dessa metoder fungerar oavsett hur filnamnen ser ut, även om det ingår mellanrum eller nyrader eller andra "konstiga" tecken.
Användarvisningsbild
brattham
Inlägg: 124
Blev medlem: 24 jul 2008, 22:43
OS: Edubuntu
Utgåva: 22.04 Jammy Jellyfish LTS
Ort: Göteborg

Re: Kopiera filer rekursivt

Inlägg av brattham »

Tack för snabbt svar. :)

Jo då, jag är väl medveten om dessa båda sätt som du nämnde ovan. Men precis som jag sa så är det lite klurigare här eftersom jag inte kan kopiera filerna direkt, utan måste ha ett steg där emellan som ska avgöra vart filerna ska kopieras: 1) Hämta filnamnen 2) Analysera filerna (först här bestäms vart de ska kopieras) 3) Kopiera filerna.

Det går alltså inte att kopiera filerna direkt eftersom jag inte vet vart de ska kopieras innan jag undersökt dem! :-\ Det är lite det som är kruxet och som gör det hela besvärligt.

/Mvh Per
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Kopiera filer rekursivt

Inlägg av Konservburk »

Dessa båda är de enda säkra metoderna om filnamnen kan innehålla lite vad som helst. Men misströsta inte, det går utmärkt att utöka det hela och stoppa in fler steg som t.ex. analyserar filnamnen innan något kopieras.

Vad är det för slags analys du vill göra lite mer exakt? Jag skulle vilja påsta att sådana "analyser" i 99 fall av 100 går att göra direkt med find. Den resterande procenten går också att få till via -exec även om det blir något mer komplicerat och förmodligen inte är helt uppenbart hur man får till om man aldrig har gjort något liknande förut.
Användarvisningsbild
brattham
Inlägg: 124
Blev medlem: 24 jul 2008, 22:43
OS: Edubuntu
Utgåva: 22.04 Jammy Jellyfish LTS
Ort: Göteborg

Re: Kopiera filer rekursivt

Inlägg av brattham »

Framför allt handlar det om vanliga mediefiler: bilder, filmer etc. Jag har gjort ett skipt som delar upp delar upp filerna i olika kataloger beroende på på mediafil och när dessa är skapade. Det funkar utmärkt då alla mediafiler ligger i samma katalog, men nu kommer jag få dem på dvd och då kommer de inte längre ligga i en och samma katalog.

Det finns ju en viss risk att filerna har samma "basename" vilket ju inte är så lyckat om det ska till samma katalog. Därför tänkte jag ta hand om alla sådana saker innan kopiering.

Men om det är det ända säkra sättet att kopiera dem som du ovan nämnde, kanske jag på något sätt kan kan kolla upp det innan (t ex genom att sortera dem och upptäcka dubbletter), för att sedan kopiera dem till en "importkatalog". Om filerna kommer till en sådan katalog så har jag ju ett skript som tar hand om dem sedan.

Jag vet inte riktigt vad som är bäst... :-\

/Mvh Per
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Kopiera filer rekursivt

Inlägg av Konservburk »

Jag är fortfarande inte riktigt med på vad du försöker göra. Det är möjligt att det går att ordna direkt med find utan större svårigheter. Men om du nu har ett skript så kan du använda den allmänna metoden med -exec och istället låta skriptet ta hand om resten:

Kod: Markera allt

find -exec /sökväg/till/skriptet {} +
Filnamnen kommer du då åt från skriptet som "$1", "$2", osv. Rent generellt så kan du loopa igenom alla filnamn så här:

Kod: Markera allt

#!/bin/sh
for i
do
   echo filnamn: "$i"
done
Det går också att skriva hela skriptet "inline" istället om du inte vill ha en separat fil:

Kod: Markera allt

find -exec sh -c '
   for i
   do
      echo filnamn: "$i"
   done
' : {} +
Användarvisningsbild
brattham
Inlägg: 124
Blev medlem: 24 jul 2008, 22:43
OS: Edubuntu
Utgåva: 22.04 Jammy Jellyfish LTS
Ort: Göteborg

Re: Kopiera filer rekursivt

Inlägg av brattham »

Okey, jag ska pröva lite, men det verkar helt klart lovande.

Jo, det är inte så svårt att förstå vad jag vill göra egentligen: Mitt skript kollar fil-extension samt skapandedatum för mediafilen t ex

1) JULTOMTE_1.JPG skapad 24 dec 2008 => foto/2008/12/24/jultomte_1.jpg
2) SKOTTE_12.AVI skapad 29 feb 2008 => film/2008/2/29/skotte_12.avi

Förr fanns filerna i samma mapp - för detta problem har jag ett skript (det gör som ovan). Numer är filerna utspridda i flera kataloger som kan finnas i flera nivåer. Det finns också en viss risk att vissa av filnamnen är identiska om vi bortser från sökvägen (då de ligger i olika kataloger). Dessa kan dock vara olika mediafiler trots att de har samma namn - dessa vill jag ej missa såklart!

Jag vill ju att kodraden som ska se till att hämta filerna ska läggas till i det gamla skriptet. Jag fattar det som att det är den senare varianten jag bör använda då? Dvs:
find -exec sh -c '
for i
do
echo filnamn: "$i"
done
' : {} +

Jag är inte riktigt på det klara med hur jag kan pilla in mina kodrader där, fast jag antar att kodraderna ska ligga mellan citatationstecknena; eller? Jag ska pröva lite fram och till baka.. :)

/Mvh Per
Senast redigerad av 1 brattham, redigerad totalt 23 gånger.
Användarvisningsbild
Konservburk
Inlägg: 5919
Blev medlem: 07 apr 2007, 22:28

Re: Kopiera filer rekursivt

Inlägg av Konservburk »

brattham skrev:Jo, det är inte så svårt att förstå vad jag vill göra egentligen: Mitt skript kollar fil-extension samt skapandedatum för mediafilen t ex

1) JULTOMTE_1.JPG skapad 24 dec 2008 => foto/2008/12/24/jultomte_1.jpg
2) SKOTTE_12.AVI skapad 29 feb 2008 => film/2008/2/29/skotte_12.avi
Hur tar du reda på skapelsedatumet? För jpeg finns väl sådant som exif-metadata, åtminstone ibland. Det beror ju lite på var bilderna kommer ifrån. Har avi-filer något liknande? Eller går du på "senast ändrad" rakt av?

Jag antar att du går på filändelsen rakt av också? Egentligen är det betydligt säkrare att istället gå på mime-typen som du kan få reda på med kommandot file.
brattham skrev:Förr fanns filerna i samma mapp - för detta problem har jag ett skript (det gör som ovan). Numer är filerna utspridda i flera kataloger som kan finnas i flera nivåer. Det finns också en viss risk att vissa av filnamnen är identiska om vi bortser från sökvägen (då de ligger i olika kataloger). Dessa kan dock vara olika mediafiler trots att de har samma namn - dessa vill jag ej missa såklart!
Jag skulle kört en hashsumma på varje fil, typ md5 eller sha1. Men det kanske är lite overkill om du inte vill hitta dubletter som råkar ha olika filnamn.
brattham skrev:Jag vill ju att kodraden som ska se till att hämta filerna ska läggas till i det gamla skriptet. Jag fattar det som att det är den senare varianten jag bör använda då? Dvs:
find -exec sh -c '
for i
do
echo filnamn: "$i"
done
' : {} +

Jag är inte riktigt på det klara med hur jag kan pilla in mina kodrader där...
Du ersätter echo filnamn: "$i" med dina kodrader. Hela filnamnet finns då i "$i".
brattham skrev:Vad har kolonet för funktion?
Det har med syntaxen för sh -c att göra. Det behöver inte vara just kolon, utan kan egentligen vara vad som helst så länge det är något. Om du plockar bort det så kommer första filnamnet att missas av for-loopen.
Användarvisningsbild
brattham
Inlägg: 124
Blev medlem: 24 jul 2008, 22:43
OS: Edubuntu
Utgåva: 22.04 Jammy Jellyfish LTS
Ort: Göteborg

Re: Kopiera filer rekursivt

Inlägg av brattham »

Det känns som projektet växer... :)

Jag använder ett litet program som heter "jhead" för att läsa av mediafilens (exif-) metadata. Där finns t ex data om mediafilens skapandedatum. För att hämta den önskade datan som "jhead" levererar använder jag AWK. Kanske finns det något bättre sätt, men det funkar. Eftersom ".avi"-filerna inte innehåller någon metadata på samma sätt (som jag känner till) som t ex ".jpg"-filerna gör, så har jag använt mig av en medföljande ".thm"-fil (det är någon slags "thumb"-fil till ".avi-filen) som innehåller metadata för ".avi"-filen. Finns inga kända metadata, använder jag mig av filens skapandedatum.

Det har funkat bra med att läsa av filextensionen förut, men nu när jag ska göra skriptet mera generellt så kan mime-typen vara bra att gå på. Fast det det är ju det har med tid ... och småbarn ... och skript-programmering ... Jag får ta max ett par saker i sänder :-\ . Annars låter det där med kontrollsumman som en intressant väg att gå!

Jag ska fundera och pröva lite till. För övrigt uppskattar jag ditt engagemang.

[ Min treåriga son har jag för övrigt lärt skriva "ls" på terminalen! Det är alltid en början... O0 ]

/Mvh Per
Skriv svar

Återgå till "Terminalforum"