bash tömbök

Létrehoztam két tömböt a readarray segítségével.
Mind a kettő fájlneveket tárol. Ez most csak tesztfájl, és nem a valódi cronnal dolgozom, csak annak egy kimásolt tartalmával!

# sh fájlok a mappákból
readarray Array1 <<< "$(find ${path} -name *.sh | awk -vFS="/" '{print $7}' | sort)"

# fájlnevek a cron tartalomból
readarray Array2 <<< "$(cat ${cron_data} | awk -vFS="/" '{print $7}' | sed -e '/^$/d' | sort)"

Ha kilistázom az első kettő tömb tartalmát, akkor úgy látom nincs hiba.

echo "${Array1[@]}"
echo "${Array2[@]}"

Egy 3. tömbbe beletettem a kettő közti különbséget.

# A cél a 3. tömbbe beletenni a két fájllista tatalmának különbségét. Tehát ha az sh fájlok már nem léteznek, de a cron tartalomban igen, akkor azokat ki kell törölni onnan.
readarray Array3 <<< "$(echo ${Array2[@]} ${Array1[@]} | tr '' '' | sort | uniq -u | sed -e 's/^ //g')"

Ezzel a sed-del kitörlöm a szó eleji szóközt. Lehet hogy az üres sort is kikéne? De attól ez még szvsz hibás.

echo "${Array2[0]}"
Itt már érdekességek adódnak. A 0 index csak egy üres sor. ???

echo "${Array2[1]}"
Ez az 1 index az első látható bejegyzés.

echo "${Array2[2]}"
Ezt meg már egy szóközzel kezdi.
Mi lehet ennek az oka?

Amikor a két tömb összehasonlításra kerül, akkor van valami gond?

Ugyanakkor egy ciklus keretén belül kiolvasom a 3. tömb tartalmát, és ami parancssorban jól működik, itt nem.
A sed: -e kifejezés #1, karakter 1: Befejezetlen regex cím - hibát ír ki. Gondolom a 0 index üressége miatt.


for name in ${Array3[@]}; do
echo nevek: ${name}
echo ${cron_data}
sleep 3
sed -i "/${name}/d" "${cron_data}"
done

Hogy lehet ezt orvosolni?

Hozzászólások

A tr '' '' mit csinál? A sed -e 's/^ //g' kifejezésben minek a g? Hátha a sor közepén megint sor eleje lesz? :)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Már annyi kísérletezésen vagyok túl, lehet hogy rosszat kopiztam át.
De azt tudni kell, hogy a fájlnevekben lehetnek szóközök. Éppen ezért, amit ki is hagytam az előbb, a sor elején van egy IFS=``
A sed -e 's/^ //g' -t azért tettem bele persze, hogy a sor eleji szóközt kivegyem. Hiszen említettem hogy a 3. tömb index 2 elején van egy szóköz.
Amúgy ez a kimenet:

sh fájlnevek a mappákból
Prime_CRON_u1515062712.sh

fájlnevek a cronból
amc_CRON_u1515531510.sh
Prime_CRON_u1515062712.sh
Viasat Explore_CRON_u1515083934.sh

3. tömb

amc_CRON_u1515531510.sh
_Viasat Explore_CRON_u1515083934.sh

prompt... S itt a Viasat előtt az alsó vonás a szóköz. Ezért van benne a sed. Ha benne van, akkor nincs szóköz. De hol van a 0 index tartalma? Miért nem az amc a 0 index?

Tudom, hogy nem a problémádra fókuszálok. A kérdésem az volt, minek a sed-be az a g? A regexp a sor elején lévő szóközre illeszkedik, s ezt cseréli semmire, azaz törli. A g azt jelenti, hogy ismételt illeszkedésnél szintén cseréljen a soron belül. Igen, de a regexp annyiban speciális, hogy előírja a sor elejére illeszkedést, az meg nem fog előfordulni a soron belül többször is. Ezért felesleges ott az a g. A tr '' '' kifejezést továbbra sem értem. Az üres karaktert cseréli üres karakterre? Semmit a semmire?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Mint említettem korábban biztos rosszat kopiztam. A tr ilyen volt, alighanem tr '' '\n'
S mivel én nem tanultam ezeket, kutatok a neten, kipróbálom az ott találtakat, és talán rájövök néhány dologra. Rengeteg kísérletezés során.
Azt is észrevettem például hogy ha nem readarrayt használok, hanem simán Array1="$(command)", akkor a szóköz problémák megszűnnek.

Van egy működő megoldásom ahol ideiglenes fájlokba tolom ki a dolgokat, de azt olvastam hogy ennél azért profibb megoldás a tömb.
De most már odáig eljutottam a tömbökkel, hogy ha pont a 3. tömbbe az összehasonlítás eredményeit a már eldobott readarray-jal teszem be. akkor már külön indexen futnak.
Úgyhogy marad a ciklus probléma.
3. tömb létrehozása

readarray Array3<<<"$(comm -1 -3 <(echo "${Array1[@]}") <(echo "${Array2[@]}"))"

Szerintem csak annyi a baj, hogy a sed '/regexp/ d' kifejezést vár, míg te változóból helyettesítesz be valamit. Ezzel csak az a baj, hogy nem literálisra keres a sed, hanem regexp-ként értelmezi, s rengeteg karakter speciális jelentéssel bír. Például a '.' - tehát pont - karakter jelentése az, hogy az bármilyen karakterre illeszkedhet. Így aztán a valami.sh nem a valami.sh-ra fog illeszkedni - bár arra is -, hanem például a valamitsh nevű stringre is. Azért volt befejezetlen a regexp, mert volt benne például egy '{', de nem volt '}', vagy megannyi más lehetőség van. Tehát ne feledd, az ott nem string konstans, hanem regexp!

Ami a file vs. változót illeti, az attól függ, mekkora a feldolgozandó adatmennyiséged. Ha kontrollálatlanul sok, több gigabyte is lehet, akkor file. Ha néhány megabyte-on belül maradsz, s ezt vagy limitálod, vagy garantálod, akkor változó, hiszen az RAM, s egyszerűbb a kezelése. Bár a disk cache miatt a file is RAM egy darabig. :) Viszont egy rakás dolog miatt sokkal bonyolultabb, rétegzettebb a file elérése, így lassabb lesz.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Pontosan mit is szeretnél végső soron? Ha a ${cron}-ban létezik egy .sh fájl, ami a ${path}-ban nem, akkor a ${cron}-ból ki akarod törölni?
Ha igen, akkor egyszerűen sorbarakod a két fájllistát, és a comm paranccsal kiszeded, ami neked kell. Valami ilyesmi:

$ find . -type f
./cron/a.sh
./cron/b.sh
./cron/c.sh
./cron/d.sh
./path/c.sh
./path/d.sh
./path/e.sh
./path/f.sh
./path/g.sh

$ comm -2 -3 <(find cron/ -type f | sed 's,.*/,,' | sort) <(find path/ -type f | sed 's,.*/,,' | sort)
a.sh
b.sh

Értem. A comm -ot is próbáltam. De talán félreértés van. Nem tudom mindegyik "fájllistát" find-del megoldani.
A find jó ahhoz hogy mely futtatható parancsok léteznek mappákban.

A másik fájllista egy szöveges fájlból van kinyerve.
Ezért most így próbáltam:
Array3="$(comm -1 -3 <(echo "${Array1[@]}") <(echo "${Array2[@]}"))"
Ez majdnem jó, csakhogy itt mind a kettő fájlnév a 0-s indexbe kerül.
A kimenet:

fájlnevek a mappákból
Prime_CRON_u1515062712.sh

fájlnevek a szöveges fájlból
amc_CRON_u1515531510.sh
Prime_CRON_u1515062712.sh
Viasat Explore_CRON_u1515083934.sh

Összehasonlítás eredménye
3. tömb
amc_CRON_u1515531510.sh
Viasat Explore_CRON_u1515083934.sh
index 0
amc_CRON_u1515531510.sh
Viasat Explore_CRON_u1515083934.sh
index 1

index 2

> A másik fájllista egy szöveges fájlból van kinyerve.
Akkor ugyanaz. Azt akarom kihozni, hogy ha tényleg az (vagy olyasmi) a feladat, amit írtam, akkor teljesen felesleges a tömbökkel való bohóckodás (minél több réteget veszel igénybe, annál több a hibalehetőség és annál több gányolás kell).
Tehát pontosan mi a feladat? Szerintem van egy 2-3 soros, tökegyszerű megoldás.

Nem is tudom. Megpróbálom úgy leírni hogy ugyanazt értsétek amit akarok.
Van egy mappa amibe futtatható fájlok vannak különböző almappákban. Ezt összegyűjtöm a find-del. Egy ${sh_fájllista} nevet nem adhatok neki, mert több fájl is lehet a mappákban.
Mit tehetek? Kirakom az eredményt 1 ideiglenes fájlba, vagy elvileg elegánsabb megoldással egy tömbbe. Melyik a jó?
Tehát van egyszer ez a fájllista. Legyen a neve "sh_fájllista"

S van a másik fájllista, melyet egy szöveges fájlból (adatok.txt) gyűjtök ki grep-pel. Itt ugyanaz a kérdés. Egy másik ideiglenes fájlba másoljam ki, vagy egy másik tömbbe? Ennek legyen a neve "kigyűjtött nevek listája"

Mind a kettőben csak fájlnevek találhatók. S ennek a két ideiglenes fájlnak vagy tömbnek a tartalmát hasonlítom össze. Miért?

Mert abban az (adatok.txt)-ben ahonnan ki grep-eltem a fájlok neveit. Vannak olyan fájlnevek, amelyek a mappákban már nem léteznek.

Mi a cél? Kitörölni ezeket a felesleges fájlneveket az (adatok.txt-ből).
Ennél világosabban nem tudom megfogalmazni.

Amit te ajánlasz, az két ideiglenes fájlra vonatkozik. Ha jól látom. Ez a járható út? Rendben.

De mint korábban említettem ezen már túl vagyok. Ez a rész megoldódott. Már megvannak az eredmények, mind ideiglenes fájlokkal, mind tömb megoldással.

Ahol most elakadtam, az a tömb használata esetén merült fel. Ideiglenes fájlok létrehozása esetén mindenre van működő megoldásom!
Tehát kiolvasom a fájlneveket a tömbből, és parancsot szeretnék végrehajtani.

for name in ${Array3[@]}; do

végrehajtom a parancsot.

sed -i "/'"${name}"'/d" adatok.txt
done

S itt hibaüzenet van. Mert ahogy már írták korábban, regexp-et vár én meg változót adtam meg a tömbből. De gőzöm nincs hogy adhatnék meg regexp-et neki.
A fájlnevek egyik példája ez:
Viasat Explore_CRON_u1515083934.sh
A sed ezt a pattern-t tartalmazó sort törölné ki az adatok.txt-ből.

A megoldási próbálkozást ne írd bele!
Tehát:

  • van egy mappa, amely almappáiban futtatható fájlok vannak
  • van egy fájllista, amely egy létező szöveges fájlból (mondjuk adatok.txt) származik
  • az adatok.txt fájlból ki kell törölni azokat a sorokat, amely egy (már) nem létező fájlra mutatnak

Pontosítások, hogy jól értem-e:

  1. az adatok.txt-ben csak fájlnevek szerepelnek, ha jól értem, elérési utak nem
  2. ha a mappa bármely almappájában van egy foo.sh futtatható fájl, akkor az adatok.txt-ben törölni kell a foo.sh sort
  3. az adatok.txt-ben szerepel még más is a fájlneveken kívül (külön sorokban), amit jó lenne nem elveszíteni és a sorrendet is megőrizni (tehát a foo.sh és a bar.sh sorok között szerepel egy "nagyon fontos információ, ne töröld ki" sor)

Azért írtam bele a próbálkozásaimat, mert senki nem hiszi el hogy próbálkozom. Mindenki azt hiszi hogy azt akarom, hogy valaki megcsinálja helyettem.

1. Az adatok.txt-ben mindenféle más adat is szerepel, mivel ez a crontab másolatom. Többek közt fájlnevek, útvonalak is időpontokkal, hogy mikor fussanak le a dolgok.
2. pipa
3. pipa

De megoldottam. Nem tudom szép megoldás-e. De működik, ráadásul tömbökkel. A regexpet nem tudtam hová tenni, de miután olvastam locsemege írását, szöget ütött a fejemben hogyha változó kell neki, akkor az talán így megkaphatja.
Bevált.
Az első tömb tartalmazza a foo.sh fájlokat. A find-del gyűjtöttem össze.
A 2. tömb tartalmazza az adatok.txt-ből kinyert fájlneveket. grep
Ezt kivonatoltam, a comm segítségével, és megkaptam a törlendő fájlneveket, amelyeket beletettem egy 3. tömbbe.

Most már csak a művelet van hátra, hogy kitöröljem a szükségtelen sorokat az adatok.txt-ből.
A name="$(echo ${name})" vel átváltottam változóvá a tömb értékét. Az awk és sed azért van, mert először azt hittem hogy a fájlnévben lévő szóköz, és a .sh végződés is bekavar. De nem.

for name in ${Array3[@]}; do
name="$(echo ${name})" # | awk -vFS="_" '{print $3}'| sed -e 's/\.sh//g')"
sed -i "/${name}/d" adatok.txt
done

Igaz benne hagyott két üres sort az adatok.txt alján. Azért kettőt, mert két bejegyzést távolítottam most el. Ha egyet távolítok el, akkor csak egy üres sor lenne pluszban.
Most megkeresem hogy ez hogy oldható meg. Mindenkinek köszönöm a figyelmét, segítségét. Sokat tanultam.
Bár a regexpet még továbbra sem értem.

Értem. Mondjuk én biztos nem tömböztem volna, hanem az adatok.txt fájlból nyert listán mentem volna végig, hogy létezik-e az a fájl. Valami ilyesmi:

grep ... adatok.txt > fajllista.txt
find . -name \*.sh | sed "s,.*/,," > letezo_fajlok.txt

while read line; do
  grep -q "${line}" letezo_fajlok.txt || sed -i "/${line}/d" adatok.txt
done < fajllista.txt

Persze lehet rajta optimalizálni, de elsőre szerintem megteszi. Ha csak egyszer kell használni, még talán jó is.

Igen. Említettem korábban, hogy van már egy működő megoldásom ideiglenes fájlokkal. Az ideiglenes fájlokon pont a txt fájlokat értettem.
S pontosan így oldottam meg, ahogy leírtad. De azt olvastam hogy a tömbök használatával el lehet kerülni az ideiglenes fájlokat. Ezért rugóztam ezen.

De a te ajánlásodban most pont a létező fájlokat töröljük le, Az összehasonlítás elmaradt. A find után kell ez:
grep -Fxvf fajllista.txt letezo_fajlok.txt > resultdata.txt
Majd a ciklusba a resultdata.txt-t olvassuk be.

De azt olvastam hogy a tömbök használatával el lehet kerülni az ideiglenes fájlokat. Ezért rugóztam ezen.

Ja, értem. Viszont amit nyertél a réven (nincsenek plusz fájlok), elveszted a vámon (feleslegesen bonyolult program).

De a te ajánlásodban most pont a létező fájlokat töröljük le,

Nem: grep -q "${line}" letezo_fajlok.txt || sed -i "/${line}/d" adatok.txt - vedd észre, hogy a grep után egy || van, ami szerint a sed-es rész akkor fut le, ha a grep nem talált egyező sort a fájlok listájában.