bash iterate-tiedostoluettelo paitsi tyhjänä

Luulin, että tämä olisi yksinkertaista – mutta se osoittautuu monimutkaisemmaksi kuin odotin.

Haluan toistaa kaikki tietyn tyyppiset tiedostot hakemistossa, joten kirjoitan tämän:

#!/bin/bash for fname in *.zip ; do echo current file is ${fname} done 

Tämä toimii niin kauan kuin sitä on vähintään yksi vastaava tiedosto hakemistossa. Jos vastaavia tiedostoja ei kuitenkaan ole, saan tämän:

current file is *.zip 

Yritin sitten:

#!/bin/bash FILES=`ls *.zip` for fname in "${FILES}" ; do echo current file is ${fname} done 

Vaikka silmukan runko ei toteudu, kun tiedostoja ei ole, saan virheen ls: ltä:

ls: *.zip: No such file or directory 

Kuinka kirjoitan silmukka, joka ei käsittele yhtään vastaavaa tiedostoa?

Kommentit

  • Lisää shopt -s nullglob ennen for-silmukan suorittamista.
  • @cuolnglm: Tämän seurauksena ls palauttaa suoritettavan komentosarjan nimen eikä tyhjää luetteloa tässä RHEL5-ruudussa (bash 3.2.25), jos teen FILES= ls * .zip ; for fname in "${FILES}"..., mutta se toimii odotetusti for fname in *.zip ; do....
  • Käytä for file in *.zip, ei `ls ...`. @cuonglm ' ehdotus on niin, että *.zip laajenee mihinkään, kun kuvio ei ole ' ei vastaa mitään tiedostoa. ls ilman argumentteja listaa nykyisen hakemiston.
  • Tässä kysymyksessä käsitellään, miksi ls -lähdön jäsentäminen on yleensä vältetään: Miksi ei jäsennä ls? ; katso myös sivun yläosassa oleva linkki BashGuide ' s ParsingLs-artikkeliin.
  • Mahdollinen kopio Miksi komentosarjan komentosarjani tukahduttaa välilyönnin tai muita erikoismerkkejä?

Vastaa

Kohdassa bash voit asettaa vaihtoehdon nullglob siten, että kuvio, joka ei vastaa mitään ”katoaa”, eikä sitä pidetä kirjaimellisena merkkijonona:

shopt -s nullglob for fname in *.zip ; do echo "current file is ${fname}" done 

POSIX-komentosarjakomennossa tarkistat vain, että fname on olemassa (ja samanaikaisesti [ -f ]: n kanssa, tarkista, että se on tavallinen tiedosto (tai symlink tavalliseen tiedostoon) eikä muita tyyppejä, kuten hakemisto / fifo / device .. .):

for fname in *.zip; do [ -f "$fname" ] || continue printf "%s\n" "current file is $fname" done 

Korvaa [ -f "$fname" ] sanoilla [ -e "$fname" ] || [ -L "$fname ], jos haluat silmukata kaikki (piilotetut) tiedostot, joiden nimi päättyy .zip, riippumatta niiden tyyppi.

Korvaa *.zip sanoilla .*.zip .zip *.zip, jos haluat ottaa huomioon myös piilotetut tiedostot, joiden nimi päättyy .zip.

Kommentit

  • shopt -s nullglob ei toimi minä Ubuntu 17.04: ssä, mutta [ -f "$fname" ] || continue toimi hyvin.
  • @koppor Kuulostaa siltä, ettet tosiasiallisesti käytä ' bash.
  • +1 POSIX-ratkaisulle.

Vastaa

set ./* #set the arg array to glob results ${2+":"} [ -e "$1" ] && #if more than one result skip the stat "$1" printf "current file is %s\n" "$@" #print the whole array at once ###or### ${2+":"} [ -e "$1" ] && #same kind of test for fname #iterate singly on $fname var for array do printf "file is %s\n" "$fname" #print each $fname for each iteration done 

Tässä kommentissa mainitset funktion kutsumisen …

file_fn() if [ -e "$1" ] || #check if first argument exists [ -L "$1" ] #or else if it is at least a broken link then for f #if so iterate on "$f" do : something w/ "$f" done else command <"${1-/dev/null}" #only fail w/ error if at least one arg fi file_fn * 

vastaus

Käytä hakua

export -f myshellfunc find . -mindepth 1 -maxdepth 1 -type f -name "*.zip" -exec bash -c "myshellfunc "$0"" {} \; 

Kuoritoimintosi on PITÄÄ viedä div id = ”f58dd7dd33″>

, jotta tämä toimisi. Nyt find suorittaa bash, joka suorittaa kuoritoiminnon, ja pysyy vain nykyisellä dir-tasolla.

Kommentit

  • joka toistuu alihakemistojen kautta, ja haluan kutsua otteluille bash-toiminnon (ei komentosarjan).
  • @symcbean I ' muokattu rajoittamaan yksittäisiä käskyjä ja käsittelemään bash-toimintoja

Vastaus

Sen sijaan /

FILES=`ls *.zip` 

Yritä:

FILES=`ls * | grep *.zip` 

Tällä tavalla, jos ls epäonnistuu (mikä se tekee tapauksessasi) se ottaa viallisen tuloksen ja palaa tyhjänä muuttujana.

current file is <---Blank Here 

Voit lisätä tähän logiikkaa, jotta se palaa ”Ei” Tiedosto löydetty ”

#!/bin/bash FILES=`ls * | grep *.zip` if [[ $? == "0" ]]; then for fname in "$FILES" ; do echo current file is $fname done else echo "No Files Found" fi 

Jos edellinen komento onnistui (poistutaan 0-arvolla), se tulostaa nykyisen tiedoston, muuten se tulosta” Ei ” Löydetyt tiedostot ”

kommentit

  • minä nk, on huono idea lisätä vielä yksi prosessi (grep) sen sijaan, että yrität korjata ongelman paremmalla työkalulla (find ) tai muuttamalla nykyisen ratkaisun asiaankuuluvaa asetusta (shopt -s nullglob)
  • OP ' -kommentin mukaan alkuperäisessä viestissä shopt -s nullglob ei toimi. Yritin find vahvistaessani vastaukseni, ja se epäonnistui jatkuvasti. Luulen Daniin viennin takia.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *