[BASH] Kopie PDF's naar server

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • X-DraGoN
  • Registratie: Juli 2005
  • Laatst online: 09:30
Hey


Ik moet even schetsen voor een beeld te krijgen met welk probleem dat ik precies zit.
Ik heb 2 servers, eentje is een Power I (IBM) en de andere is een standaard Ubuntu Linux.
Op de IBM is een beperkte 'PASE' omgeving beschikbaar, wat eigenlijk AIX is met wat IBM special sauce over.
Vroeger werden de PDF's waar het over gaat, via SMB protocol overgezet, maar dat is niet veilig genoeg beoordeelt, dus moest daar een andere oplossing voor komen.
PASE ondersteunt SSH en dus ook het SCP commando. Let wel, het gaat hier per over 40.000 tot 200.000 files per locatie (er zijn een 45 tal locaties) die gekopieerd moet worden.

Ik heb public / private key pair authenticatie werkend gekregen en ik kan dus scripting gebuiken om de files over te zetten.

Voorwaarde is verder nog dat er een check gedaan word dat de files ouder als 30 dagen moeten zijn vóór ze gekopieerd mogen worden (daarna volgt 15 dagen later een delete)

Ik heb een script gemaakt dat een lijst gaat opvragen van alle files per locatie op de server en die overbrengt naar de Linux server.
Op de Linux server wordt dan een vergelijk gedaan met welke files reeds op de locatie staan daar wordt dan een diff op gedaan om de uiteindelijke lijst vast te stellen.

Daarna volgt dan de scp opdracht per file om de effectieve overzetting te doen.

Sinds ik de functie geïntegreerd heb om te vergelijken neemt mijn opdracht heel veel en heel lang CPU weg, maar werkt wel uiteindelijk.

Vraag: is er een betere manier om dit te doen?

Bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/bin/bash
set -e
#
userdir="*knip*"
#sla de huidige datum op
start=$(date +"%y-%m-%d_%T")
#tel 30 dagen terug en kijk dan in welke map (per jaar) dat de PDF moet komen.
jaar=$(date --date "-30 days" +'%Y')
#get userid
user=$(id -u -n)

#verwijderd de eerste 2 characters van de gegenereerde lijst en sla die op als opgeruimd.txt
cut -c 3- /srv/data/$userdir/INVOICES/t.list > /srv/data/$userdir/INVOICES/opgeruimd.txt

# Step 1: Read the contents of opgeruimd.txt and store in an array
mapfile -t files_to_copy < /srv/data/$userdir/INVOICES/opgeruimd.txt

#check of de DIR reeds bestaat waar we in gaan schrijven en anders maak ik hem aan
if [ ! -d /srv/data/$userdir/INVOICES/"$jaar" ]; then
  mkdir -p /srv/data/$userdir/INVOICES/"$jaar";
fi

# Step 2: List the files in the target directory
target_directory="/srv/data/$userdir/INVOICES/"$jaar"/"  # Replace with your target directory
existing_files=("$target_directory"/*)

# Step 3: Compare the two lists and filter out existing files
filtered_files=()
for file in "${files_to_copy[@]}"; do
    exists=false
    for existing_file in "${existing_files[@]}"; do
        if [[ "${file##*/}" == "${existing_file##*/}" ]]; then
            exists=true
            break
        fi
    done
    if [ "$exists" = false ]; then
        filtered_files+=("$file")
    fi
done

#lees de file opgeruimd.txt in de variabele
#value=`cat /srv/data/$userdir/INVOICES/opgeruimd.txt`

#test of mijn value leeg is en schrijf dan een log weg om te laten zien dat ik wel degelijk gelopen heb
if [ -z "$file" ]
        then
echo "Script ran ok, but nothing to copy" >> log"$start".txt
exit 0
        else
#for loop om door elke value in de file heen te lopen en te kopiëren naar een voor gedefinieerde plaats
for file in "${filtered_files[@]}"; do
#for value in $value
#do
        scp $user@*naam_server*:/$userdir/FACTUREN/${file##*/} /srv/data/$userdir/INVOICES/"$jaar"/${file##*/}

if [ $? != 0 ]; then
        echo "copy unsuccessfull of file "${file##*/} >> error"$start".log
else    echo "I am copying "${file##*/}"..."
        now=$(date)
        echo "copied "${file##*/} "at" "$now" >> log"$start".txt
fi
done
fi
exit

Beste antwoord (via X-DraGoN op 26-09-2023 09:59)


  • MartinMeijerink
  • Registratie: Juli 2008
  • Laatst online: 15-09 18:12

MartinMeijerink

Computerrorist

Het probleem zit vooral in de dubbele lus (regel 29 t/m 40), hier ga je (minimaal) 40000 maal 40000 keer twee stringmanipulaties doen (twee keer padnaam verwijderen in regel 32), dat kost veel tijd. Beter kun je van tevoren ervoor zorgen dat bestanden al zonder padnaam in de lijst staan, dan hoef je die stringmanipulatie niet binnen de lus te doen. Die lus moet dus zo klein mogelijk, en als het kan ook geen dubbele lus doen, dus op regel 32 iets als:
code:
1
test -e "$target_directory/$file" && exists=true && break

Regel 37-39 kan in één regel, ook geen double-quotes nodig:
code:
1
test $exists = false && filtered_files+=($file)

Dit lijkt een heeele kleine snelheidswinst, maar zit wel binnen de lus, dat maakt de snelheidswinst dus heel groot! En er valt eigenlijk nog veel meer te optimaliseren, maar dan zou ik het hele script herschrijven, dat laat ik dan ff weer aan jou over :)

An unbreakable toy is useful to break other toys

Alle reacties


Acties:
  • +5 Henk 'm!

  • BernardV
  • Registratie: December 2003
  • Laatst online: 15-09 10:54
Ik zou voor rsync gaan via ssh.
https://www.ibm.com/suppo...solution-environment-pase

Met Rsync kun je makkelijk bestanden syncen.

Voorbeeldje hoe je PDF bestanden ouder dan 30 dagen sync:
code:
1
rsync -av --progress --files-from=<(find ./ -type f -iname '*.pdf' -mtime +30) ./ /path/to/target/or/remote/folder

Acties:
  • +2 Henk 'm!

  • Hero of Time
  • Registratie: Oktober 2004
  • Laatst online: 15-09 22:42

Hero of Time

Moderator LNX

There is only one Legend

Ik zou ook gaan voor rsync en was m'n eerste gedachte toen ik je post aan het lezen was, gevolgd door 'check of rsync beschikbaar is voor IBM'.

Voor je script heb ik geen verbeteringen, zo geweldig kan ik ook niet direct scripten (en heb ook niet veel tijd genomen eerlijk gezegd :P). Enige opmerking die ik heb is dat je geen '# commentaar' moet gebruiken aan het eind van uitvoerende zaken of definities van variabelen. Je weet nooit of het per ongeluk toch nog mee komt.
De Debian package maintainers van Thunderbird hebben hier eens een fout mee gemaakt toen ze in een migratiescript van Icedove naar Thunderbird gingen en "commandos \ #comments" gebruikten. De '\' was bedoelt om aan te geven dat het commando op de volgende regel verder ging, maar dat pakte toch wat anders uit omdat er een teken na kwam, waardoor het als escape teken werd geïnterpreteerd.

Vandaar dat comments beter op eigen regels moeten en buiten eventuele uitvoer blokken. Binnen een functie kan nog.

Commandline FTW | Tweakt met mate


Acties:
  • Beste antwoord
  • +1 Henk 'm!

  • MartinMeijerink
  • Registratie: Juli 2008
  • Laatst online: 15-09 18:12

MartinMeijerink

Computerrorist

Het probleem zit vooral in de dubbele lus (regel 29 t/m 40), hier ga je (minimaal) 40000 maal 40000 keer twee stringmanipulaties doen (twee keer padnaam verwijderen in regel 32), dat kost veel tijd. Beter kun je van tevoren ervoor zorgen dat bestanden al zonder padnaam in de lijst staan, dan hoef je die stringmanipulatie niet binnen de lus te doen. Die lus moet dus zo klein mogelijk, en als het kan ook geen dubbele lus doen, dus op regel 32 iets als:
code:
1
test -e "$target_directory/$file" && exists=true && break

Regel 37-39 kan in één regel, ook geen double-quotes nodig:
code:
1
test $exists = false && filtered_files+=($file)

Dit lijkt een heeele kleine snelheidswinst, maar zit wel binnen de lus, dat maakt de snelheidswinst dus heel groot! En er valt eigenlijk nog veel meer te optimaliseren, maar dan zou ik het hele script herschrijven, dat laat ik dan ff weer aan jou over :)

An unbreakable toy is useful to break other toys


Acties:
  • 0 Henk 'm!

  • X-DraGoN
  • Registratie: Juli 2005
  • Laatst online: 09:30
Hey @MartinMeijerink

Ik heb je suggesties toegevoegd aan het script en het werkt inderdaad 1000x sneller. Bedankt!

Ik snap alleen niet zo goed waar mijn denkfout zat in het script? Als je wil, kan je dat even uitleggen?

Bash:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/bin/bash
set -e
#
userdir="*knip*"
#sla de huidige datum op
start=$(date +"%y-%m-%d_%T")
#tel 30 dagen terug en kijk dan in welke map (per jaar) dat de PDF moet komen.
jaar=$(date --date "-30 days" +'%Y')
#get userid
user=$(id -u -n)

#verwijderd de eerste 2 characters van de gegenereerde lijst en sla die op als opgeruimd.txt
cut -c 3- /srv/data/$userdir/INVOICES/t.list > /srv/data/$userdir/INVOICES/opgeruimd.txt

# Step 1: Read the contents of opgeruimd.txt and store in an array
mapfile -t files_to_copy < /srv/data/$userdir/INVOICES/opgeruimd.txt

#check of de DIR reeds bestaat waar we in gaan schrijven en anders maak ik hem aan
if [ ! -d /srv/data/$userdir/INVOICES/"$jaar" ]; then
  mkdir -p /srv/data/$userdir/INVOICES/"$jaar";
fi

# Step 2: List the files in the target directory
target_directory="/srv/data/$userdir/INVOICES/"$jaar"/"
existing_files=("$target_directory"/*)

# Step 3: Compare the two lists and filter out existing files
filtered_files=()
for file in "${files_to_copy[@]}"; do
    if ! test -e "$target_directory/${file##*/}"; then
        filtered_files+=("$file")
    fi
done
#lees de file opgeruimd.txt in de variabele
#value=`cat /srv/data/$userdir/INVOICES/opgeruimd.txt`

#test of mijn value leeg is en schrijf dan een log weg om te laten zien dat ik wel degelijk gelopen heb
if [ -z "$file" ]
        then
echo "Script ran ok, but nothing to copy" >> log"$start".txt
exit 0
        else
#for loop om door elke value in de file heen te lopen en te kopiëren naar een voor gedefinieerde plaats
for file in "${filtered_files[@]}"; do
#for value in $value
#do
        scp $user@*knip*:/$userdir/FACTUREN/${file##*/} /srv/data/$userdir/INVOICES/"$jaar"/${file##*/}
if [ $? != 0 ]; then
        echo "copy unsuccessfull of file "${file##*/} >> error"$start".log
else    echo "I am copying "${file##*/}"..."
        now=$(date)
        echo "copied "${file##*/} "at" "$now" >> log"$start".txt
fi
done
fi
exit

oh, en @Hero of Time zijn suggestie heb ik ook even gedaan, door de # weg te halen na een regel

Acties:
  • 0 Henk 'm!

  • MartinMeijerink
  • Registratie: Juli 2008
  • Laatst online: 15-09 18:12

MartinMeijerink

Computerrorist

X-DraGoN schreef op dinsdag 26 september 2023 @ 09:56:
Hey @MartinMeijerink

Ik heb je suggesties toegevoegd aan het script en het werkt inderdaad 1000x sneller. Bedankt!
Ok, np :)
Ik snap alleen niet zo goed waar mijn denkfout zat in het script? Als je wil, kan je dat even uitleggen?
Helaas kan ik geen gedachten lezen, weet dus ook niet waar het in jouw denken fout ging. ;)
Maar ik heb je TS 3x gelezen, en ik denk dat ik er 95% van heb begrepen, in ieder geval genoeg om wat aanwijzingen te geven. Eerst dacht ik ook meteen aan rsync, maar vraag me af hoe we dat gaan doen als je alleen bestanden wilt kopieren die ouder dan 30 dagen zijn. Dan zou je nog steeds voor elk bestand rsync aanroepen, dus dan heeft het al geen nut om rsync te doen, en is scp net zo handig.

Wel zou je het op een quick-and-dirty manier kunnen doen met rsync:
1. Eerst de hele mikmak met rsync naar je eigen server kopieren (lekker snel):
code:
1
rsync -a $user@*naam_server*:/$userdir/FACTUREN/ /srv/data/$userdir/INVOICES/$jaar/


2. Vervolgens alles wat 30 dagen of nieuwer is van je eigen server weer wissen.
code:
1
find /srv/data/$userdir/INVOICES/$jaar/ -mtime 29 -exec rm {} \;

De vraag is alleen of dit mag, want je hebt ze dan toch heel even op je eigen server gehad.
En de vraag is ook hoeveel data je nou onnodig hebt gekopieerd, als je 2/3 van wat je hebt gekopieerd meteen weer weg moet gooien is het mss niet echt rendabel.

An unbreakable toy is useful to break other toys


Acties:
  • +1 Henk 'm!

  • MartinMeijerink
  • Registratie: Juli 2008
  • Laatst online: 15-09 18:12

MartinMeijerink

Computerrorist

Misschien is sshfs nog wel het beste, nog even installeren als je het nog niet hebt:
apt install sshfs

Hiermee kun je de remote map mounten in je lokale filesystem:
mkdir -p /mnt/facturen
sshfs $user@*naam_server*:/$userdir/FACTUREN/ /mnt/facturen/

Nu kun je naar hartelust alles op jouw manier van de (lokaal geworden) map /mnt/facturen/ naar /srv/data/$userdir/INVOICES/$jaar/ kopieren, bijvoorbeeld met find:
find /mnt/facturen/ -mtime +29 -exec cp -a {} /srv/data/$userdir/INVOICES/$jaar/ \;

Na afloop ff umounten:
umount /mnt/facturen

An unbreakable toy is useful to break other toys

Pagina: 1