Beetaversioon: sisu on enamasti terviklik ja avalikuks katsetamiseks valmis, kuid vajab veel tehnilist, keelelist ja kasutatavuse kontrolli.

Peatüki vaade

Linux/Unix/macOS käsurea kiirõpik

Praegu loed peatükki `find` ja `xargs` ohutumalt, mis kuulub osasse Osa IV: Tekst, otsing ja automatiseerimine.

find ja xargs ohutumalt

Loogika

find leiab teed.

xargs annab need teed järgmisele käsule edasi.

Probleem tekib siis, kui failinimedes on:

  • tühikud
  • tabulaatorid
  • reavahetused

Kui kasutada naiivset kuju, siis võib üks failinimi laguneda mitmeks tükiks. Sellepärast on oluline paar:

  • find ... -print0
  • xargs -0

Kiirülevaade

Eesmärk on failipuus tegutseda nii, et tühikud ja muud keerulised failinimed tulemust ei lõhuks.

Käsk või kujuMilleksMida tavaliselt näed
find ...leia kirjeid tingimuste järgileitud failide ja kataloogide teed
xargsanna teed järgmisele käsulejärgmise käsu väljund
-print0eralda nimed nullmärgigamasinloetav voog, mitte tavalised read
xargs -0loe nullmärgiga nimesidtühikutega nimed jäävad terveks
-exec ... {} +käivita käsk otse find seesjärgmise käsu väljund

Vale eraldusviis lõpeb sageli veateatega No such file or directory, sest failinimi lõigatakse tükkideks.

Tüüpilised algaja vead

  • eeldatakse, et kõik failinimed on lihtsad ühe sõnaga nimed
  • kasutatakse xargs-it pimesi seal, kus -exec oleks lihtsam
  • testimata käsk pannakse kohe muutma või kustutama

Käivita need käsud


find . -type f -name '*.txt'
find . -type f -name '*.txt' -print0 | xargs -0 wc -l
find . -type f -name '*.log' -exec ls -lh {} +

Kui tahad näha just keeruliste nimede loogikat, tee väike näide:


mkdir -p find-naide
cd find-naide
touch 'üks fail.txt' 'teine fail.txt'
find . -type f -name '*.txt' -print0 | xargs -0 ls -l

Miks -print0 ja -0 tähtsad on

Vaikimisi eraldatakse käsurea tekst sageli tühikute ja reavahetuste järgi. See tähendab, et fail nimega:


minu fail.txt

võib naiivses töövoos käituda nagu kaks eri sõna.

Selle vältimiseks:

  • find -print0 eraldab tulemused nullmärgiga
  • xargs -0 loeb just seda vormi

See on praktiline „ohutu vaikevariant“, kui liigud käsult find järgmise käsuni.

xargs või -exec?

Mõlemad on kasulikud.

Näide xargs-iga:


find . -type f -name '*.txt' -print0 | xargs -0 wc -l

Näide -exec-iga:


find . -type f -name '*.txt' -exec wc -l {} +

Rusikareegel:

  • kui tahad lihtsat ja ohutut kuju, siis -exec ... + on sageli väga hea
  • kui tahad teadlikult ehitada torupõhist töövoogu, siis -print0 | xargs -0 on tugev valik

xargs vs while read

On veel üks väga kasulik kuju:


find data -type f -name '*.txt' -print0 |
while IFS= read -r -d '' fail; do
  wc -l "$fail"
done

Selle loogika on:

  • find -print0 annab failinimed ohutult edasi
  • read -r -d '' loeb ühe nullmärgiga eraldatud faili korraga
  • tsüklis saad iga faili kohta teha rohkem kui ühe sammu

xargs on väga hea siis, kui:

  • tahad ühe käsu kiiresti paljudele failidele anda

while read on eriti hea siis, kui:

  • tahad iga faili kohta teha väikese loogika
  • vajad tsüklit, tingimust või mitut käsku järjest

Näiteks:


find data -type f -name '*.txt' -print0 |
while IFS= read -r -d '' fail; do
  echo "Töötlen: $fail"
  head -n 1 "$fail"
done

Väldi pimesi hävitavaid käske

find ja xargs lähevad eriti ohtlikuks siis, kui lõppu pannakse:

  • rm
  • chmod
  • mv

Seetõttu tasub alati alustada ohutu eelvaatega:


find . -type f -name '*.log'

ja alles siis lisada lõppu faile muutev tegevus.

Veel parem on alustada mittehävitava käsuga, näiteks:


find . -type f -name '*.log' -print0 | xargs -0 ls -lh

Päris näide: loenda kõik data/ tekstifailid kokku

Repo kataloog data/ on turvaline koht find-i harjutamiseks.


find data -type f -name '*.txt' -print0 | xargs -0 wc -l

Siin:

  • find leiab kõik .txt failid
  • -print0 eraldab need ohutult
  • xargs -0 wc -l annab igale failile reaarvu

See on hea näide, sest siin ei ole vaja midagi kustutada ega muuta, ainult vaadata.

Päris näide: keeruliste nimedega failid

Kui tahad näha, miks -print0 ja -0 on päriselt vajalikud, tee selline väike töökaust:


mkdir -p otsi-naide/'Tallinn andmed'
cp data/sample-words.txt 'otsi-naide/Tallinn andmed/sonad 1.txt'
cp data/sample-text.txt 'otsi-naide/Tallinn andmed/tekst 2.txt'
find otsi-naide -type f -name '*.txt' -print0 | xargs -0 ls -lh

Selle töövoo mõte on:

  • failinimedes on tühikud
  • naiivne xargs võiks need lõhkuda
  • -print0 | xargs -0 hoiab need tervikuna koos

Päris näide: leia logifail ja vaata selle suurust


find data -type f -name '*.log' -exec ls -lh {} +

See on väike, aga päris praktiline muster:

  • leia failid
  • ära hävita midagi
  • vaata enne suurust või arvu

Just nii peakski find-iga töötamine algama.

Tühi tulemus ja suured failihulgad. find ... -exec käsk {} + ei käivita käsku, kui vasteid pole. xargs -0 korral sõltub tühja sisendi käitumine süsteemist; GNU xargs võib käsu siiski käivitada. GNU valik -r väldib seda, kuid pole igal pool sama tähendusega. Suure hulga korral võib nii -exec ... + kui ka xargs jagada failid mitmeks käivituseks: näiteks wc kokkuvõte on siis partii, mitte tingimata kogu puu summa.

read -r -d '' näide eeldab Bashi või zsh-d, mitte suvalist POSIX sh-d. IFS= säilitab nime alguse ja lõpu tühikud, -r kurakriipsud ning tühi -d määrab nullmärgi eraldajaks.

Minitest

  1. Loo vähemalt üks fail, mille nimes on tühik.
  2. Leia see fail find-iga.
  3. Näita seda faili ls -l abil läbi töövoo -print0 | xargs -0.
  4. Seleta ühe lausega, miks -print0 ja -0 koos käivad.
  5. Loenda kataloogi data/ tekstifailide read käsuga find ... -print0 | xargs -0 wc -l.
  6. Tee sama töövoog uuesti kujul while IFS= read -r -d '' fail; do ...; done.

Peatüki täisspikker

Töövood

Eesmärk

otsi failid find-iga ja anna need ohutult edasi järgmisele käsule, isegi siis, kui nimedes on tühikuid

Põhikujud

  • find . -type f -name '*.txt'otsi failid
  • find . -type f -name '*.txt' -print0 | xargs -0 wc -lloe read ohutult
  • find . -type f -name '*.log' -exec ls -lh {} +käivita otse
  • find data -type f -name '*.txt' -print0 | while IFS= read -r -d '' fail; do head -n 1 "$fail"; donetöötle ükshaaval

Olulisemad lipud, märgid ja kiirnupud

  • -type ffind: ainult tavafailid
  • -name '*.txt'nime järgi
  • -print0find: failinimede vahel nullmärk
  • xargs -0xargs: eralda argumendid nullmärgiga
  • -exec käsk {} +käivita otse
  • read -r -d ''Bash/zsh: loe nullmärgini, säilita kaldkriipsud