Kirjoittaja Aihe: Ohjeita shell-skriptaukseen (bash)  (Luettu 329614 kertaa)

nm

  • Käyttäjä
  • Viestejä: 16232
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #180 : 07.01.15 - klo:20.22 »
Moi, olen luonut pienen skriptin, joka symlinkkaa Kuvat-kansion jne kotikansiosta, joka on SSD'llä, normaalille, pyörivälle kiintolevylle. Se on kuitenkin "sidottu" siihen, että käyttiksen/kansioiden pitää olla suomen kielellä. Miten saisin järkevämmin toteutettua sen niin, että symlinkkaus onnistuu, oli kielenä mikä tahansa? Siis niin, että kotikansion "normaali" rakenne ei muutu.

Hakemistot on määritelty kunkin käyttäjän kotihakemiston tiedostossa ~/.config/user-dirs.dirs:

Koodia: [Valitse]
$ cat ~/.config/user-dirs.dirs

# This file is written by xdg-user-dirs-update
# If you want to change or add directories, just edit the line you're
# interested in. All local changes will be retained on the next run
# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
# absolute path. No other format is supported.
#
XDG_DESKTOP_DIR="$HOME/Työpöytä"
XDG_DOWNLOAD_DIR="$HOME/Lataukset"
XDG_TEMPLATES_DIR="$HOME/Mallit"
XDG_PUBLICSHARE_DIR="$HOME/Julkinen"
XDG_DOCUMENTS_DIR="$HOME/Asiakirjat"
XDG_MUSIC_DIR="$HOME/Musiikki"
XDG_PICTURES_DIR="$HOME/Kuvat"
XDG_VIDEOS_DIR="$HOME/

Tiedoston voi ladata skriptin alussa . -komennolla, jolloin muuttujia voi käyttää normaalisti skriptin sisällä:

Koodia: [Valitse]
#!/bin/sh

. "$HOME/.config/user-dirs.dirs"

echo "$XDG_PICTURES_DIR"

Toki voi olla fiksua myös tarkistaa, että muuttujalla on jokin arvo, ja asettaa joku oletushakemisto tai varoittaa käyttäjää:

Koodia: [Valitse]
if [ -z "$XDG_PICTURES_DIR" ]; then
    echo "Warning: XDG_PICTURES_DIR not set, defaulting to $HOME/Pictures"
    XDG_PICTURES_DIR="$HOME/Pictures"
fi

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #181 : 23.01.15 - klo:12.13 »
BASH:illa ei pidä käsitellä mitään vaan ainoastaan antaa käsiteltävät ulkoisten ohjelmien käsiteltäviksi. Esimerkiksi kaksi seuraavaa lausetta, jotka aikaansaavat aivan saman asian elikä tekstijonon perättäisten samojen merkkien supistamisen yhdeksi:

echo $tekstijono | sed 's/\(.\)\1\+/\1/g'
set -- $(echo $tekstijono | grep -Eo '(*.)\1+'); for n in $@; do tekstijono=$(echo ${tekstijono/$n/${n:0:1}}); done; echo $tekstijono

Ensimmäinen käyttää sed-ohjelmaa ja se on nopea ja luotettavan oloinen. Siinä BASH toimiikin vain sed:in kutsujana.
Toinen pää-asiassa kikkailee BASH:illa ja se on hidas eikä oikein luotettavan oloinen.

- ongelmana on vain se, että vaikka BASH:ia osataankin enää vain jotenkin niin sekä sed:in mutta varsinkin awk:in taitajia ei enää ole ollenkaan ja on pakko tehdä BASH:illa ontuvia skriptejä.
- tiesitkö muuten että awk on paljon-paljon edistyneempi skriptauskieli kuin BASH ?
- hakutermin *. kuuluu tässä tapauksessa olla kirjoitettu noin; kyseessä on Mikrosoft-Linux logiikan ero.
**
BASH tuntee myös "globien-laajennokset", esimerkiksi "rekursiiviset *:n". Ne ovat hitaita eikä niitä kannata käyttää, mutta silti on syytä tuntea ne. Jostain syystä halusin nähdä kuinka homma toimii koska olin unohtanut kaiken muun paitsi että se on koneessani kuvattuna ja että siinä on merkkiyhdistelmä ** . Etsintä täytyi kokeilla ja löytihän sen regex:  \*\\* .
- extglobit ovat seuraavat:
Koodia: [Valitse]

  Extglob           Regex                           Kuvaus toiminnasta
?(lista)          (...|...)?     nolla tai yksi lista
*(lista)          (...|...)*     nolla tai useampia listoja
+(lista)          (...|...)+     yksi tai useampia listoja
@(lista)          (...|...)      täsmälleen yksi lista
!(lista)                         "kaikki muu kelpaa paitsi lista"
**                               "rekursiivinen *"

- merkki | on merkitykseltään tai (OR)
- shopt -s extglob; cat ~/**/*   # listaa koko kotihakemiston kaikki tiedostot
- ajaaskel osasi kertoa että extglob on päätteessä oletuksena päällä, mutta skripteissä ei
**
regex on ahne, se valitsee aina pisimmän mikä sopii ehtoon. Joskus kuitenkin halutaan erottaa erikseen kaikki sopivat. Esimerkki tästä lazy-regex:ästä:
  echo "123 abc 456 def 789" | grep -o -P [0-9].*[a-z]   # tulostaa: 123 abc 456 def
  echo "123 abc 456 def 789" | grep -o -P [0-9].*?[a-z]  # tulostaa kaksi riviä: 123 c   ja: 456 d
« Viimeksi muokattu: 22.03.15 - klo:05.11 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #182 : 23.01.15 - klo:12.37 »
"tr" tekee tuon pienellä vaivalla:

Koodia: [Valitse]
a=12233344455555
 tr -s [[:alnum:]] <<< "$a"
12345

Ei tuo paha ole Bashilläkään:

Koodia: [Valitse]
for ((i=0; i<${#a};i++)); do [[ "$vm" != "${a:i:1}" ]] && echo -n "${a:i:1}";vm="${a:i:1}"; done;echo
12345

Tuossa yllä ajetaan silmukkaa niin että mennään sanan merkit yksitellen läpi. Silmukka alkaa nollasta (joka on ensimmäinen merkki sanassa) ja päättyy kun sanan pituus saavutetaan. Viimeksi käsitelty merkki talletetaan muuttujaan "vm" ja seuraava merkki tulostetaan vain jos se on eri kuin "$vm".  Vertailu on tehty käyttäen
Koodia: [Valitse]
[[jokin_ehto]]  &&
syntaksia.
« Viimeksi muokattu: 23.01.15 - klo:13.52 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #183 : 23.01.15 - klo:19.06 »
Tuo tr ... on kyllä nopea ja elegantti, mutta en saa sitä käsittelemään ääkkösiä.
- erikoismerkkit ja välilyönnit hoituvat kun muuttaa tuon [:alnum:]:in [:print:]:iksi, mutta ääkkösiä sitä ei saa käsittelemään mitenkään.
- hieman tutkittuani asiaa se johtuu jostakin LC_TYPE jutuista, mutta kuulemma se on vielä pahempi kuin IFS ja jos kokeilee sillä saa järjestelmänsä helposti sekaisin.

Sensijaan tuo
Koodia: [Valitse]
for ((i=0; i<${#a};i++)); do [[ "$vm" != "${a:i:1}" ]] && echo -n "${a:i:1}";vm="${a:i:1}"; done;echo
käsittelee kaikkia on vielä nopeakin - riippuen tekstistä joskus nopeampi ja joskus hitaampi. Mutta ei kumpikaan sed:ille pärjää varsinkin kun sed toimii semmoisenaan tiedostoillekin nopeudenkaan kärsimättä.

Tuota nopeuden mittausta ihmettelen kaikkienkin skriptien kanssa; skriptiajuri nimittäin lisää aina ilmanmuuta kaikkiin skripteihin time-käskyn ja  niin sen tuloksiin kiinnittääkin huomiotaan paremmin - kun määrää käsin time:n skriptin ajokäskyn eteen niin se antaa kyllä samat tulokset mutta sitä ei tule käytettyä aina. Tulokset saattavat vaihdella paljonkin kun nopeus on pieni,
esimerkiksi tulos voi vaihdella välillä 5-15 ms. Ja ajettaessa skripti päätteessä ensimmäisen keran on tulos usein paljon hitaampi kuin toistot - ihankuin tulkki oppisi tai jotakin olisi cacheissa ja joskus cachet nollautuisivat omia aikojaan.
- toki niin että sadasta mittauksesta 90 on 5-8 ms ja vain muutama on 12-15 ms.
- muuten ainoastaan tuo sed-versio osaa käsitellä sanan edessä olevia välilyöntejä.
**
Vastaan tuli sellainen ongelma että matriisiin perään piti liittää  toinen matriisi poistaen samalla duplikaatti-rivit. Ratkaisuksi tuli:

Koodia: [Valitse]
#!/bin/bash
# matriisien yhteenliittäminen poistaen duplikaatit petteriIII 4.2.2015
# matriisien määrittelytapa on normaali ja merkkivalikoima on melkein rajoittamaton. Muuten tässä skriptissä on vain kaksi riviä asiaa
# ja loput ovat vain oikeellisuuden tarkastelua.
a[0]="kukkivat/'   kummut" # matriisien arvot annetaan näin jotta alkuasetelma on varmasti yksiselitteinen. Erikoismerkit hyväksytään kun edessä on keno.
a[1]=3
a[2]="pähkäilyä komentorivi-komennoista!"
a[3]=5$
b[0]=1
b[2]="kukkivat/'   kummut"
b[1]=2.57e9
b[2]=3
b[3]="pähkäilyä komentorivi-komennoista!"
b[4]=4
c=("${a[@]}"); c+=("${b[@]}")    # matriisien yhteenliittäminen tapahtuu selväpiirteisimmin lisäämällä matriisi kerrallaan.
c=("$(echo -e "${c[@]/%/\\n}" | awk 'x[$0]++ == 0' )") # duplikaattien poistaminen
echo "${c[0]}"  # tulostetaan yhteen-nivottu matriisi c tällätavalla ettei jää pienintäkään epäilystä että jokin toimii omituisesti
echo "${c[1]}"
echo "${c[2]}"
echo "${c[3]}"
echo "${c[4]}"
echo "${c[5]}"
echo "${c[6]}"
echo "${c[7]}"
**
Tein skriptin nimeltään: tagien välisen tekstin etsiminen tiedostosta . Sen nopeus perustuu awk:iin ja saattaa muut kielet häpeään - ei sikäli että se olisi edes yhtä nopea kuin muut vaaan siksi että se on jo kymmeniä vuosia maannut haudassa ja vieläkin se täytyy ottaa huomioon. Seuraavan haku-skriptin hitaus johtuu melkein kokonaan tiedoston lukemisesta minkä kesto- " aika lienee melko sama kaikilla kielillä. Sed puolestaan tarjoaa helpon muuteltavuuden - seuraavat ominaisuudet saa näillä muutamilla lisillä:

Teksti voi olla yhdessä pötkössä tai siinä saa olla rivinsiirtoja kaikkialla muualla paitsi etsittävissä sano:issa. Siten voidaan etsiä vaikka wget:in tulosteesta jos vain saadaan etsitävät sana:t määriteltyä.

Mikäli teksti on riveissä niin haku-sanait saavat olla samalla tai eri riveillä ja ensimmäisen sana:n edessä saa olla mitävain tai ei mitään samoinkuin toisen sana:n perässä saa olla mitävain tai ei mitään.
Tekstissä saa olla myös rivejä jotka eivät ole haku-sanojen välissä eivätkä ne sekoita olipa niitä kuinka monta vaan ja olipa niillä mitähyvänsä, vaikka tyhjää.
Sekään ei ole virhetilanne että tiedostossa ei ole etsittäviä sanoja ollenkaan.

Tulosteessa on löydettyjen rivien välissä tyhjä rivi. Mikäli samassa löydöksessä on useampi rivi niin niiden välissä ei ole tyhjää riviä jotta näkisi mitkä rivit kuuluvat yhteen. Samassa etsinnässä ryhmiissä riviluku voi siis vaihdella.

Ja ennenkaikkea: merkkivalikoimaa ei ole mitenkään rajoitettu.
Muuten kaikki väittää, ettei awk tunne BASH:in muuttujie ellei niitä sille erikseen esitellä. Mutta hyvin näyttää onnistuvan.

Koodia: [Valitse]
#!/bin/bash
function haku () {
# mikäli tyhjien rivien tulostuminen halutaan estää lisätään "löytöloitsujen" perään: | awk 'NF'
[[ $tag = [KkYy] ]] && < $tiedosto sed "s/$tag1/\n$tag1/g" | awk "/$tag1/,/$tag2/" | sed "s/.*$tag1//g" | sed "s/$tag2.*/\n/g"  ||
< $tiedosto sed "s/$tag1/\n$tag1/g" | awk "/$tag1/,/$tag2/" | sed "s/.*$tag1/$tag1/g" | sed "s/$tag2.*/$tag2\n/g" ; }

[[ -f /tmp/haku ]] && read tiedosto tag1 tag2 tag <<< $(cat /tmp/haku)
read -ep "minkänimisestä tiedostosta etsitään: " -i " $tiedosto" tiedosto
read -ep "mistä sanasta haku alkaa: " -i " $tag1" tag1
read -ep "mihin sanaan haku loppuu: " -i " $tag2" tag2
read -ep "poistetaanko hakusanat tulosteesta: " -i " $tag" tag
echo $tiedosto $tag1 $tag2 $tag > /tmp/haku
echo
haku

**
Aloinpa tehdä testinkäsittely-funktioita. käytännössä senjälkeen kun nuo funktiot ovat koneessasi tuntee koneesi lisää käskyjä, esimerkiksi:
 
1. Sen paikan kertominen jossa teksti-palanen tekstirivillä on. Kutsumuoto:
   etsi_paikka "kolmekymmentä merkkiä edessä_koti_perässämitävaan" koti     

2. Tekstirivillä olevien reaalilukujen|kirjain-jaksojen|erikoismerkki-joukkojen|ip-osoitteiden ... irroittaminen. Kutsumuoto esimerkiksi:
   etsi_luvut  "mvkjngöejgn-2.2e-3 .33lfmbäamg"
   - kirjoittaa luvut näytölle ja myös asettaa ne matriisin peräkkäisten jäsenien arvoiksi.
   - lukujen välissä tulee olla jotakin - välilyöntikin kelpaa.
   - integerit, desimaaliluvut ja järjestysluvut kelpaavat myös.

3. tekstirivillä rajoittimien välissä oleva tekstin tulostaminen. Kutsumuoto:
   etsi_tekstit_tagien_välissä tag1 tag2 tiedostopolku
   - siis se käy läpi koko tiedoston ja antaa joukon tuloksia.
   - kirjoittaa tekstit näytölle ja myös asettaa ne matriisin peräkkäisten jäsenien arvoiksi.
   - tagit saavat olla samalla tai eri riveillä eikä se vaikuta tulostukseen.
   - merkkivalikoimaa ei rajoiteta, jopa kova lainausmerkki kelpaa vaikka yksikseen.
   - etsittävä voi olla joko riveissä tai yhtenä pötkönä, joten vaikka wget-tulosteesta voi etsiä, sillä samalla rivillä voi olla monta tag-paria.
**
Yksi tärkeä ohjelma joka mielestäni on BASAH:ista puuttunut on sellainen haku jossa etsittävää ei täydy määrätä tarkasti vaan esimerkiksi sallien hakusanassa kahden merkin eron. Luuloa se oli, sillä kyllä BASH:issa sellainen on. Nimeltään se on agrep ja Ubuntussa se kuulemma jopa toimii.
**
Vihdoinkin sain selvityksen time-komennon "real/user/sys"-arvoista . Yksinkertaisessa koneessa: real on kokonaisaika, user on aika joka on vietetty user-prosesseissa ja sys on aika joka on vietetty sys-prosesseissa joten silloin: real=user+sys+"aika joka on kulunut odotellessa", noin suurinpiirtein.  Mutta semmoista kuin "yksinkertainen kone" ei olekaan, varsinkin nykyään multi-prosessointi sotkee pahasti, sillä sys-ajaksi lasketaan kaikissa säikeissä kuluneiden aikojen summa. Siten sys saattaa olla ajoista suurinkin. 
- aikojen suhteet kertovat aina vaan vähemmän siitä kannattaako skriptiä yrittää nopeuttaa.
**
Pähkäilin matriisien erilaisuuden tulostavaa skriptiä ja etsiessäni netistä vihjeitä parhaista tavoista kohtasinkin näpsäkän awk-skriptin ja ihmettelin taas kertaalleen senjälkeen että kun tehtävälle on jo löytynyt hyvä ratkaisu esitetään paljon huonompia eikä ole opittu mitään. Mutta toisaalta awk-skriptejä ei oikein käsitä joten tein BASH-toteutuksen jossa on se hyvä puoli että sen toiminnan käsittää kukahyvänsä ja kun siinä ole looppia on se melko kelvollinenkin:
Koodia: [Valitse]
diff <(echo -e ${matriisi1[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) <(echo -e ${matriisi2[@]/%/\\n} | awk '{$1=$1} !x[$0]++' | sort) | grep [\<\>] | tr -d  [\<\>]  | awk '{$1=$1}1'
-  awk '{$1=$1} !x[$0]++'   poistaa etu-välilyönnit ja toistot

Mutta itse asia on se, että jos BASH-skriptissä on looppi-käskyjä on se hidas ja alkeellisesti tehty, sillä BASH käsittelee aina joukkoja joten looppi on sijoitettu jokaisen käskyyn sisälle eikä sitä tarvitse itse erikseen määrätä. Varsinkin awk, sed, grep ja xargs ovat varsinaisia joukonkäsittelijöitä ja niissä on sisäiset loopit.
- no ei se looppien käyttäminen täysin kelvotonta ole sillä looppeja käyttävien skriptitien toiminta on helpompi ymmärtää. On elämäntehtävä oppia skriptaamaan ilman looppeja, minä itse en ainakaan ole oppinut.
**
automaatit olivat tehneet tosisuuren tiedoston, jossa monet rivit ja kappaleet olivat monta kertaa eikä ihminen saanut asioista enää mitään selvää. Duplikaattirivit oli helppo poistaa niinkin että kappaleiden väliin jäi yksi tyhjä rivi. Mutta tämä kasasi tuhansia näennäisesti tyhjiä rivejä tiedoston loppuun; näennäisesti tyhjiä rivejä sillä  riveillä saattoi olla TAB:beja, kontrollimerkkejä ja mitä lie. Mutta kyllä siihenkin ratkaisu löytyi: koko toiminta kuvattuna:
Koodia: [Valitse]
cat ohje | awk '!NF || !x[$0]++' | awk '!NF {if (++n <= 2) print; next}; {n=0;print}'
 
- misiköhän noiden awk-lauseiden nopeus täytyisi laittaa ? Kun ne vievät aikaa tässä tapauksessa -200 millisekuntia.
- yleensäkin skriptien ajankäyttö tuntuu kummalliselta: mitatut ajat vaihtelevat ihmeellisesti ja suuremmat kokonaisuudet tuntuvat toimivan huomattavasti nopeammin kuin osiensa summa. Mutta toisaalta BASH esimerkiksi jakaa prosessorikuormaa eri ytimien kesken ilmanmuuta.
**
BASH:issa on huomattava määrä valmiiksi määriteltyjä funktioita. Selvitin funktion in_array kutsun:
Koodia: [Valitse]
a=(1 "2 3" 4 5); in_array "2 3" "${a[@]}" && echo joo || echo ei

Maailmalta löytyykin samanlaisia toteutuksia ziljoona, mutta itseasiassa skriptissä in_array on lukemattomia puutteita ja päälle päätteeksi looppi. Esimerkiksi seuraava karkea loopiton skripti olisi parempi:
Koodia: [Valitse]
matriisi=(1 glmnämthn 87 2 82 7 89 asd 81); etsittava=8[0-9]; echo -n 'löydöt muodossa: alkio/mitä_siellä_on: '; echo -e ${matriisi[@]/%/\\n} | grep -no $etsittava | xargs echo | tr : /
**
Perättäisten merkkien poistamiseksi tekstistä käsky: tr -s [[:print:]] <<< "$a" ei kelpaa, sillä vaikka se on tehokas niin se ei toimi skandien tai välilyöntien suhteen ja erikoismerkit saavat sen ihan sekaisin. Kaikki muu toimii kyllä käskyllä: cat tiedosto | tr -s [[:print:]] paitsi skandien kanssa se ei toimi sittenkään.

Toimiva toteutus on sed-skripti:
Koodia: [Valitse]
sed 's/\(.\)\1\+/\1/g' ohje
- se on huomattavast hitaampi kuin: tr -s [[:print:]] , vaan kaikki kelpaa: välilyönnit, erikoismerkit, skandit ....
- echo:a pehmeiden lainausmerkkien kanssa, printf:ää tai <<< ei voi käyttää mikäli tekstissä on erikoismerkkejä - tai toimivathan ne kun jokaisen erikoismerkin eteen kirjoitetaan keno.
  Kyllä echo:takin voi mikäli lauseessa ei ole erikoismerkkejä.
**
- googlen regex-haku toimii ainakin jotenkin. Monessa muussakin haussa olen käyttänyt sitä mutta esimerkiksi haku: bash awk \$0=
löysi myös mitä pitikin.
**
itseopiskelu johtaa mielipuolisiin tilanteisiin. Olenko vielä unesta sekaisin kun luulen etten ole tämmöistä tavannut: kun näppäilen koneeseenii:  a=echo; $a c      niin se tulostaa: c

Edelleenkin olen sitä mieltä että tästä ei ole puhuttu missään. Vaikenemisella saattaa olla joku syykin, esimerkiksi se että tuommoinen johtaa erittäin tehokkaisiin itseään muuttaviin ohjelmiin jotka herraties miksi ovat täydellisessä pannassa.
Mutta nyt kun käyttäytyminen selvisi niin selityskin löytyi: BASH-skriptissä voi olla muuttuja missävaan. Kun tulkki kohtaa rakenteen: $muuttuja niin se korvaa rakenteen välittömästi sen arvolla ennenkuin tekee mitään muuta.

Nopeasti pieni esimerkki:
Koodia: [Valitse]
#!/bin/bash
function tehdään_mitä_määrätään () {
$@
}

tehdään_mitä_määrätään ls
read -p "paina return saadaksesi seuraavan tulosteen"
tehdään_mitä_määrätään ls -l
read -p "paina return saadaksesi seuraavan tulosteen"
tehdään_mitä_määrätään sudo blkid | awk '{print $1}' | tr -d : | sed 's,/dev/,,g' # tulosta kovalevyn tunnetut osiot
« Viimeksi muokattu: 24.10.16 - klo:11.21 kirjoittanut petteriIII »

Whig

  • Käyttäjä
  • Viestejä: 333
  • puppu-generaattori
    • Profiili
    • localhost
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #184 : 17.03.15 - klo:16.46 »
Minulla on scripti joka hakee cu:lla USB liitännäisestä palikasta (WatchPort/H) lämpötilan ja ilmankosteuden.
Lämpötila saadaan sanomalla palikalle T ja ilmankosteus sanomalla H.
Scripti toimii täysin lämpötilan kanssa eli T:llä mutta jos vaihdan T:n tilalle H:n eli kyselisin ilmankosteutta en saa minkäänlaista outputtia.
Lueskelin tuota cu:n ohjetta: https://docs.freebsd.org/info/uucp/uucp.info.cu_Commands.html ja siinä on kohta:
" To send an escape character to
the remote system at the start of a line, it must be entered twice.  All
commands are either a single character or a word beginning with `%'
(percent sign)."

Lämpötila, kun on muodossa +29.5625C ja kosteus 38% niin voiko tuo % merkki palikan outputissa sotkea CU:ta? Ja jos niin mitenköhän tuon saisi kierrettyä?
puppu-generaattorin outputtia
"minä olen kansainvälinen supertähti"

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #185 : 17.03.15 - klo:19.00 »
"cu": n dokumenttisi mukaan komennot lähetetään tilde-alkuisina eli ~<jotain>. Koodisi näkeminen saattaisi auttaa, ei tuosta arvaamaan pysty mitään.
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

Whig

  • Käyttäjä
  • Viestejä: 333
  • puppu-generaattori
    • Profiili
    • localhost
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #186 : 17.03.15 - klo:20.29 »
"cu": n dokumenttisi mukaan komennot lähetetään tilde-alkuisina eli ~<jotain>. Koodisi näkeminen saattaisi auttaa, ei tuosta arvaamaan pysty mitään.

Tässä tämä koodi:



#!/bin/bash
# # This script reads the temparature value from Watchport/T Temperature sensor
# which is connected to the USB port.
 
# Working on Centos 5 Linux, run by crontab
# Change '\rT\r' (Celcius) to '\rTF\r' to get Fahrenheit
t_name=T1 # Sensor name
t_timesec=5
t_parity=none
t_speed=115200
t_file1=/var/log/watchport1.tmp
logfile=/var/log/watchport1.log
t_USBport=/dev/ttyUSB0
 
if [ ! -f ${logfile} ]; then
 echo "Sensor;Date;Time;Temperature">$logfile
fi
 
t_time=`date +%H:%M`
t_date=`date +%d:%m:%Y`
 
# Remark file is Binary !
(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Ja tuo tosiaan toimii OK mutta jos tuon \rT\r vaihtaa \rH\r niin eipä tule enää logiin mitään (.tmp:n tulee samat connect ja disconnect jutut, kuin T:tä käyttäessä).
puppu-generaattorin outputtia
"minä olen kansainvälinen supertähti"

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #187 : 18.03.15 - klo:08.56 »
Se kosteus ei varmaan ala plusmerkillä rivin alussa kuten koodisi seuloo "grep": llä ?

(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Kannattaa lisätä tuonne testauksen ajaksi rivi joka tulostaa tilapäisen tiedostosi "$t_file1" sisällön ennen kuin käsittelet sitä millään tavalla jolloin näkee missä menee väärin.
« Viimeksi muokattu: 18.03.15 - klo:09.01 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

Whig

  • Käyttäjä
  • Viestejä: 333
  • puppu-generaattori
    • Profiili
    • localhost
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #188 : 18.03.15 - klo:11.14 »
Se kosteus ei varmaan ala plusmerkillä rivin alussa kuten koodisi seuloo "grep": llä ?

(echo -e '\rT\r' ; sleep $t_timesec; ) |cu --parity=$t_parity -l $t_USBport -s $t_speed dir>$t_file1 2>/dev/null
t_temperature=`cat -A $t_file1 | grep ^+|sed -e 's/\^.*//'`
echo "$t_name;$t_date;$t_time;$t_temperature">>$logfile
exit 0

Kannattaa lisätä tuonne testauksen ajaksi rivi joka tulostaa tilapäisen tiedostosi "$t_file1" sisällön ennen kuin käsittelet sitä millään tavalla jolloin näkee missä menee väärin.

Kas... joskus aikaisemminkin tappelin tuon kanssa ja huomasin tuon kohdan mutta nyt oli menny täysin ohi.
Testaan tuota poistaen tuon grep:n tai muuttaen sen hakemaan sen %:n loppuvan rivin. Palailen asiaan jos ongelma ei tällä ratkennut.
Kiitän =)


Edit:
Jep. Tällä korjaantui kiitoksia =)
« Viimeksi muokattu: 19.03.15 - klo:07.48 kirjoittanut Whig »
puppu-generaattorin outputtia
"minä olen kansainvälinen supertähti"

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #189 : 21.03.15 - klo:20.09 »
Kun päätteessä kirjoittaa: text="aaa<skipme>bbb<skipmetoo>ccc"; echo "${text//<*([^>])>/}" niin tulostuu aaabbbccc
Mutta kun kirjoittaa sen skriptiin niin tulostuu: aaa<skipme>bbb<skipmetoo>ccc   niin BASH:illa, DASH:illa kuin SH:llakin.

Mikä on syynä ? Tai onko tämä alkavaa dementiaa ?
« Viimeksi muokattu: 21.03.15 - klo:20.56 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #190 : 21.03.15 - klo:23.13 »
Kun päätteessä kirjoittaa: text="aaa<skipme>bbb<skipmetoo>ccc"; echo "${text//<*([^>])>/}" niin tulostuu aaabbbccc
Mutta kun kirjoittaa sen skriptiin niin tulostuu: aaa<skipme>bbb<skipmetoo>ccc   niin BASH:illa, DASH:illa kuin SH:llakin.

Mikä on syynä ? Tai onko tämä alkavaa dementiaa ?

Tuo johtuu siitä että ympäristössä on eroa kun ajetaan skriptiä päätekomennon sijaan eli "extglob" ei ole oletuksena päällä kun ajat skriptiä vaikka se suoraan komentoriviä käytettäessä on.

Voit korjata ongelman lisäämällä skriptisi alkuun

Koodia: [Valitse]
shopt -s extglob
« Viimeksi muokattu: 21.03.15 - klo:23.16 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #191 : 22.03.15 - klo:04.58 »

Tuo johtuu siitä että ympäristössä on eroa kun ajetaan skriptiä päätekomennon sijaan eli "extglob" ei ole oletuksena päällä kun ajat skriptiä vaikka se suoraan komentoriviä käytettäessä on.

Voit korjata ongelman lisäämällä skriptisi alkuun
Koodia: [Valitse]
shopt -s extglob

Tattista taas kertaalleen. Kyllä on todella kiva että foorumilla on virtuooseja joilta saa vastauksen tämmöiseenkin.
- taas sain uutta päätteen käyttöön: päätteessä voi antaa käskyn: ls ~/**/* ja se listaa koko kotikansion
**
Mutta edelinen konsti ei toimi kun "tag" merkit ovat erilaisia, esimerkiksi << .... >> mutta tässä skriptissä mitkä hyvänsä käyvät kunhan ne vain kirjoittaa skriptiin.
Välilyönnit ovat sallittuja myös tagien välissä.
- eihän tag:ien lukumäärä ja muutkaan oletukset eivät ole mistään kotoisin. Mutta niiden huomioiminen merkitsee vain muutamia koodi-rivejä lisää.
- liian aikaista kokeilla wget:iä.

Koodia: [Valitse]
#!/bin/bash
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
kaikkien_tagien_tekstit=($(echo $teksti | grep -Po \<\<.*?\>\>))
ekan_tagin_teksti=$(echo ${kaikkien_tagien_tekstit[@]} | awk -F' <<' '{print $1}')
tokan_tagin_teksti=$(echo ${kaikkien_tagien_tekstit[@]} | awk -F' <<' '{print $2}')
echo "$teksti" | sed "s/$ekan_tagin_teksti//g" | sed "s/<<$tokan_tagin_teksti//g"
« Viimeksi muokattu: 22.03.15 - klo:20.07 kirjoittanut petteriIII »

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #192 : 22.03.15 - klo:21.49 »
En ole aivan varma tarkoititko tätä:

Koodia: [Valitse]
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
sed 's/<<[^>]*>>//g' <<< "$teksti"
a a abbbc c c

eli halusit edelleen tiputtaa pois tuplaväkästen rajaaman tekstin ? Et tosin varsinaisesti kysynyt mitään --- mutta vastasin nyt kuitenkin kun skriptausjutut ovat hauskoja :)

Selitys:
Tuossa on käytetty "sed" -komennon (sed = string editor) "etsi ja korvaa" muotoa ja kauttaviivat osoittavat minne se etsitty ja korvaava teksti rajautuu.  Etsimme yhdistelmää joka alkaa kahdella väkäsellä vasemmalle "<<", sitten mitä hyvänsä merkkejä kunhan ei ole oikeaväkänen "[^>]", niitä voi olla peräkkäin miten monta vain "*" paitsi että pitää päättyä kahteen väkäseen oikealle ">>". Jos tuosta tulee osuma niin se korvataan "ei millään" eli kauttaviivojen välissä ei ole mitään "//".  Korvataan "ei millään" = poistetaan. 
Komennon alussa oleva "s" tarkoittaa "separator" eli erotinmerkki, ässän jälkeen heti tuleva merkki eli tässä tapauksessa kauttaviiva erottaa kenttiä kuten etsittävä ja korvattava.  Voisimme halutessamme käyttää jotain muutakin merkkiä sen tilalla:

Koodia: [Valitse]
sed 's|<<[^>]*>>||g' <<< "$teksti"
a a abbbc c c

Tuo "g" lopussa tarkoittaa että ei luovuteta ensimmäiseen osuman jälkeen vaan toistetaan samaa korvausta jos uusi osuma löytyy (kuten tuossa esimerkissä kävikin).

Aivan lopuksi, noilla kolmella väkäsellä vasemmalle voimme lähettää muuttujan sisällön "sed": lle melkein samalla tavalla kuin echo "$teksti " | sed ....
Sanoin "melkein" koska tätä pidetään joskus parempana tapana.   Erot putkituksella "|" ja "HERE string" tavalla tehdä asia ovat:
1) Putkitus avaa uuden ympäriston missä ajetaan (subshell).  Saat vietyä muuttujien sisällön putkeen mutta takaisin et saa muuttujan kautta mitään eli et pysty mitenkään putken aikana muuttamaan niiden muuttujien sisältöä jotka olivat olemassa ennen putken ajoa, muutosta ei välitetä.
2) Ainut rajoitus mikä tulee mieleen HERE stringin kanssa on se että lopun LF merkistä (hexa 0a) ei pääse eroon eli "<<<" lisää aina loppuun LF merkin, vertaa:

Koodia: [Valitse]
hexdump -C <<< 'a'
00000000  61 0a                                             |a.|
00000002

echo -n 'a' | hexdump -C
00000000  61                                                |a|
00000001

http://unix.stackexchange.com/questions/20157/why-does-a-bash-here-string-add-a-trailing-newline-char
« Viimeksi muokattu: 23.03.15 - klo:11.24 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #193 : 23.03.15 - klo:08.55 »
En ole aivan varma tarkoititko tätä:
Koodia: [Valitse]
teksti="a a a<<jotain tekstiä>>bbb<<jotain toista tekstiä>>c c c"
sed 's/<<[^>]*>>//g' <<< "$teksti"

- justiinsa tuota tarkoitin. Lisäksi homma on sopii useampaankin tapaukseen pienin muutoksin. Oppirahat on kalliit. Tattista taas

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #194 : 23.03.15 - klo:09.58 »
Lainaus
- justiinsa tuota tarkoitin.

Laitoin hieman selitystä tuonne vastaukseeni niin siitä on hyötyä muillekin asiasta kiinnostuneille.
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #195 : 23.03.15 - klo:11.21 »
Seuraavaksi kiinnostuin kuinka tuosta samasta lauseesta saa pelkästään nuo väkästen väliset tekstit. Mutta koska sed:issä ei ole negaatiota niin en keksinyt kuinka sen saisi sed:illä tehtyä - kuulemma se onnistuu kyllä jotenkin takaisinpäin-viittauksen avulla ja varmaankin se onnistuu jotenkin toisinkin. Mutta grep:illä se on:
Koodia: [Valitse]
grep -Po \<\<.*?\>\> <<< "aaa<<jotain tekstiä>>bbb<<jotain toista tekstiä>>ccc"

- noitten grepin tag:ien väsäämisessä joutuu käyttämään kenoja
- jos haluaa tulosteet samalle riville niin  kirjoitetaan:
(grep -Po \<\<.*?\>\> | tr '\n' ' ' ; echo) <<< "aaa<<jotain tekstiä>>bbb<<jotain toista tekstiä>>ccc"'
- grepin haussa oleva ? muuttaa grepin käyttäytymisen: normaalisti haku on "greedy" elikä suurin mahdollinen, ja ? tekeekin hausta "lazy":n elikä ensimmäinen tulos kelpaa (=PCRE ?). Oikeastaan samaan liittyy tuo parametri -o joka käskee tulostamaan pelkästään hakutuloksen ja perään tulee rivinsiirto sillä kun haku jatkuu heti löydön perästä uudestaan niin seuraava löytö tulostetaan eri riville.
**
Aina uutta ja ihmeellistä. Mikäli << ja >> voivat olla tiedoston eri riveillä niin käskyksi tuli:
Koodia: [Valitse]
cat tiedosto | sed 's/<</\n<</g' | sed 's/>>/>>\n/g' | sed -e '/<</,/>>/d'
- kyllä se samalla rivilläkin olevat poistaa.
- noiden:  | sed 's/<</\n<</g' | sed 's/>>/>>\n/g'   tarkoitus on säilyttää: << edessä-  ja: >> perässä-oleva teksti.
 


« Viimeksi muokattu: 23.03.15 - klo:18.27 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #196 : 25.03.15 - klo:21.03 »
Olenko oikeassa siinä että jos skripti on BASH:issa niin BASH:in tulkitessa se ei välitä ollenkaan siitä puhutaanko tukittavassa sed:istä, awk:ista vai jostain muusta vaan suoritaa ainoastaan omaa koodiaan, esimerkiksi kohdatessaan ilmaisun: $muuttujan_nimi se kirjoittaa sen tilalle muuttujan arvon - ja tämäntakia sed:in jne. kuvaus suljetaan kovien lainausmerkkien sisään ettei BASH muuttaisi mitään.

siten vaihdettaessa lainausmerkit pehmeiksi käsky:
Koodia: [Valitse]
a=1; b=2; echo 1 | sed "s/$a/$b/g"

tulostaa 2. Sellaisen väitteen olen lukenut että vaikka pehmeät lainausmerkit toimivat useimmiten nekin niin lainausmerkkejä ei pitäisi muuttaa pehmeiksi ilman pätevää syytä. Mutta mitään väitteen perusteita ei kerrottu.

ajaaskel

  • Palvelimen ylläpitäjä
  • Käyttäjä
  • Viestejä: 3401
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #197 : 25.03.15 - klo:21.36 »
Ei siinä asiassa ole muuta juonta kuin että osalle merkeistä on annettu erikoismerkitys.  Jos käytetään pehmeää lainaista (tavalliset lainausmerkit) niin kysessä on osittainen suojaus ja kova lainaus (yksinäiset hipsukat) puolestaan on täysi suojaus eli merkkiä ei tulkita miksikään muuksi kuin mitä se on.

Esimerkki:

Koodia: [Valitse]
teksti="kissa"

echo "$teksti"
kissa

echo '$teksti'
$teksti

eli taalan (tai minkä tahansa merkin) erityismerkitys estetään kovalla lainauksella.  Tästä ja muista tilanteista on tarkemmin vaikkapa täällä:

http://wiki.bash-hackers.org/syntax/quoting

Pehmeä lainaus estää koodiasi menemästä rikki jos muuttujalle annettu arvo sisältää välilyöntejä mutta kova lainaus muuttaisi jo merkityksen kuten yllä.
« Viimeksi muokattu: 25.03.15 - klo:21.41 kirjoittanut ajaaskel »
Autamme ilolla ja ilmaiseksi omalla ajallamme.  Ethän vaadi, uhoa tai isottele näin saamasi palvelun johdosta.

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #198 : 30.03.15 - klo:06.49 »
Tämäpä selvä.
**
AWK-skriptien tekemiseen löytyy ohjeita vielä vähemmän kuin BASH-skripteille vaikka se onkin paljon parempi skriptaus-kieli. Awk:ia voi kuitenkin käyttää BASH:ista. Mutta Ubuntu käyttää awk:in versiota nimeltään mawk ja sillä on yksi suuri puute: se ei tunne "inplace" editointia; sellaista niinkuin esimerkiksi sed. Gawk:in saa asennettua  käskyllä: sudo apt-get install gawk . Inplace editointi ei kuitenkaan toimi ihan samoin kuin sed:issä vaan esimerkiksi: 
gawk -i inplace '{print $1}' tiedosto .
Tai jos haluaa tehdä ensin bakupin niin: gawk -i inplace -v INPLACE_SUFFIX=.bak '{print $1}' tiedosto
**
Awk on tosiaan loistava. Esimerkiksi ohjelma joka poistaa tiedostosta duplikaatti-rivit on: awk '!x[$0]++'. Siinä on siis koko nopea ohjelma joka käsittelee koko tiedoston, olipa se vaikka giga-kokoa. Tottamaar awk:illa on puutteita - eihän muuta voi odottaakaan iki-vanhalta skriptauskieleltä jota ei päivitetä juuri koskaan. Mutta parannuksia toki löytyy, esimerkiksi:
awk '{$1=$1} !x[$0]++' jossa rivien alussa tai lopussa olevat välilyönnit eivät vaikuta. Tai:
awk '!NF || !x[$0]++' joka jättää tyhjät rivit rauhaan.

- $0 on koko rivi. Sen paikalle voidaan laittaa myös kentän numero tai kenttälista; esimerkiksi: awk '!x[$2,$3]++'
- BASH:in uniq ei kelpaa, sillä se edellyttää sorttausta.
- tuon awk '!x[$0]++' toiminnan selostus lienee yksinkertainen: awk:in kaikki matriisit ovat assosiatiivisia elikä niiden osoite voi olla tekstiä. Tassä assosiatiivisen matriisin x jäseneksi jonka osoite on tekstirivi yritetään asettaa jotakin. Jos asettaminen onnistuu niin jäsen tulostetaan (koskei toimenpide-kenttää ole niin suoritetaan oletus-toiminne joka on print) ja siirrytään seuraavaan tekstiriviin - ja jos asetus ei onnistu koska sillä osoitteella on jo jäsen niin siirrytään vain seuraavaan tekstiriviin tulostamatta mitään.
**
Olen tässä ihmetellyt regex:än osumien lukumäärää rajoittavia määreitä, esimerkiksi a{2\} jonka pitäisi rajoittaa a-kirjaimien maksimimäärän kahteen. Mutta esimerkiksi:
echo aaaa | egrep a{2\} ilmoittaa että löytyihän niitä a-kirjaimia. Mutta kyse taitaakin olla siitä, että regex:n omalta kannaltaan se tekee täsmälleen sen minkä lupaakin; se on vain minun tulkintani ettei neljää a:ta saa olla. Mielestäni tämä selviää kun egrep-käskyyn lisää määreen -o : echo aaaa | egrep -o a{2\}   
joka tulostaa kahdelle riville aa; siis regex hakee merkkijonoa aa ja kun löytää sen niin tulostaa sen ja jatkaa hakua ja kun löytää taas niin tulostaa sen ja jatkaa hakua ja kun ...

- osoittautui että on tosivaikeaa tehdä regex joka tarkistaa että "grepattavassa" on esimerkiksi täsmälleen kaksi perättäistä samaa merkkiä ja kolme tai enemmän hylätään. Mutta seuraava viritelmä toimii:
echo abc       däägghijklmnopq***r   söööuvvwxyyyz | grep -Eo '(.)\1{1,}' | sed -rn '/(.)\1{2,}/!p'
- Mitkä merkit hyvänsä kelpaavat, ainoastaan regex:än numeroita muutetaan eivätkä toiset merkkiryhmät sotke.
- välilyönnit eivät sotke toiminta. Tosin niitä ei kyllä näytetäkään.
- tyypiltään: grep  "\<[0-9]\{5\}\>" kelpaavat vain silloin kun etsityn luvun molemmin puolin on välilyönti tai tekstijonon alku tai loppu.
**
Olen muutamat päivät tutkinut sed:iä ja awk:ia ja on osoittautunut että kumpikin osaa oikein määriteltynä tehdä mitä vaan, mutta että kaikki on todella liukasta käsiteltävää.
**
taas kertaalleen vastaan tuli vanhojen kernelien poistaminen, ja se tuntuu olevan jatkuvasti muutteleva ikuisuusongelma sillä synapticilla niitä ei viitsi poistaa vaan yhdellä kootulla käskyllä.
- käyttämäni käskyn: sudo apt-get purge $(dpkg -l linux-{image,headers}-"[0-9]*" | awk '/ii/{print $2}' | grep -ve "$(uname -r | sed -r 's/-[a-z]+//')")   pitäisi toimia myös silloin kun kone on niin täynnä vanhoja kerneleitä että se menee tukkoon. Päätteeseen pääsy ja käskyn kopiointi jostain ovat kylläkin ongelmia, mutta ratkaistavissa ne ovat. Kaikki nämä järjestelmää "korjaavat" käskyt muuten ovat myös ongelma sillä ne korjaavat joskus koneen boottaus-kelvottomaksi.   
- onko tosiaan mahdollista ettei tähän turhien kerneleiden ongelmaan ole aina toimivaa ratkaisua ? Sillä voiko olla mitään muuta syytä ettei sitä ole lisätty recoveryn vaihtoehtoihin ? Tai synapticiin ? Tai peräti boottaukseen siten että vain uusin ja sen edellinen jätetään ?
**
hakukoneet ontuu. Kun nimittäin etsin tietoa operaattorista nimeltään ? (if...then...else-rakenne jossa if:iä ei kirjoiteta olenkaan, ? on then ja : on else) sain etsiä ilman että oikeat osumat häiritsivät. Kunnes sitten tajusin etsiä operaattoria nimeltään ternary. Yksi esimerkki lopputuloksesta:
Koodia: [Valitse]
a=1; (( a ? b=1 : (b=2) )); echo $bsen sanallinen kuvaus: jos muuttujalla a on arvo niin b saa arvon 1 ja jos a:lla ei ole arvoa niin b saa arvon 2. Mutta awk:illa se on mukavampi käyttää:
Koodia: [Valitse]
echo "1 2" | awk '{print ($1 < $2 ? "kakkularatsis" : "ja hilipatahoi")}'sillä se pystyy palauttamaan myös tieteellisen esitysmuodon tai tekstijonon samoinkuin vertailemaan niitä.

Käytännön ongelma: tiedoston kentässä 2 voi olla "0 tai 1" taikka "tosi tai epätosi". Alunperin kentässä on joko 1 tai 0 ja ne halutaan muuttaa arvoiksi tosi tai epätosi.
Ratkaisuun on helpointa tehdä awk-skripti (tämä on vain toimintaperiaatteen kuvaus):
Koodia: [Valitse]
awk '{$2=$2=="1" ? "tosi" : "epätosi"}{print $0}'
Mikäli tämmöinen ehto täytyy koodata BASH:illa kannattaa tehdä se näin:
Koodia: [Valitse]
[[ vertailu ]] && ... || ... **
Tiedostojen erojen tulostus on myös yksi ikuisuus-ongelma jolle ei sikäli kunnollista ratkaisua voi ollakaan koska jo sen määritteleminen on vaikeaa mitä eroavaisuudella tarkoittaa ja on usein mahdotonta tehdä ohjelma joka määritykset toteuttaa. Kuvittelisin että ainakin seuraavat ominaisuudet ohjelmalta tulee löytyä:
1. Tyhjät rivit eivät saa sotkea mitään, siis rivi-riviltä vertaaminen ei kelpaa.
2. Mikäli tiedostoa editoidaan siten, että jokin tekstinosa siirretään toiseen paikkaan ei se saa aiheuttaa eroa vertailussa.
3. Tiedostojen eroavaisuus tulee esittää esittää jomman-kumman tiedoston kannalta.

Nämä vaatimukset täyttää awk-skripti joka kertoo mitä tiedostossa1 on sellaista mitä ei löydy tiedostosta2 :
Koodia: [Valitse]
awk 'NR == FNR{a[$0];next} !($0 in a)' tiedosto1 tiedosto2 - skripti on hyvä runko josta lähteä tekemään lisä-määrittelyjä.
- tällätavoin awk:issa käsitellään kahden tiedoston ongelmia muutoinkin: esimerkiksi siirretään tietoja tiedostojen välillä, tarkistetaan ovatko uuteen tiedostoon kerätyt tiedot samansuuntaisia kuin päätiedostossa ...
- menetelmän periaatetta voi soveltaa vaikka kuinkamonen tiedoston nopeaan käsittelyyn.
**
Erilaisuutta määriteltäessä itse tekstirivin asia on joskus sama, mutta tekstirivin eteen tai perään on lisätty välilyöntejä ja tällöin hälytyksen erilaisuudesta soisi jäävän pois, sillä turhia hälytyksiä tulee muutenkin liikaa. awk:in menetelmä etu- ja jälkivälilyöntien poistamiseksi on käsky $1=$1 jolloin koko käskystä tulee:
Koodia: [Valitse]
awk 'NR == FNR{$1=$1;a[$0];next} {$1=$1}!($0 in a)' tiedosto1 tiedosto2**
Aamupäivän päivitys sotki ääkkös-käyttäytymisen. Iltapäivän  päivitys taisi palauttaa sen toimivaksi.
- muutenkin toiminta muuttui aavistuksen verran. Aivan kuin expansio-sääntöjä olisi viilattu.
« Viimeksi muokattu: 12.04.15 - klo:08.43 kirjoittanut petteriIII »

petteriIII

  • Käyttäjä
  • Viestejä: 657
    • Profiili
Vs: Ohjeita shell-skriptaukseen (bash)
« Vastaus #199 : 21.04.15 - klo:12.02 »
Olen äimänkäkenä ja joudun taas kysymään neuvoa: kohtasin verkkosivun joka väitti koodinsa olevan BASH:ia. Esimerkiksi yksi koodinpätkä oli tällainen:
Koodia: [Valitse]
function stack_destroy
{
    : ${1?'Missing stack name'}
    eval "unset _stack_$1 _stack_$1_i"
    return 0
}

Kysymys: mikä virka on tuolla kaksoispisteellä rivillä: 
: ${1?'Missing stack name'}

- kasasin skriptin ja koetin. Ilman kaksoispistettäkin toimi, mutta jonkinsortin virheen se silloin tekee. En siis edelleenkään tiedä kaksoispisteen merkitystä, mutta senverran kuitenkin että se on tarpeellinen.
« Viimeksi muokattu: 21.04.15 - klo:15.37 kirjoittanut petteriIII »