Bash-komentosarjavirhe merkkijonoissa, joiden poluilla on välilyöntejä ja jokerimerkkejä

Minulla on vaikeuksia saada Bash-komentosarjojen perusteet alas. Tässä on tähän mennessä:

#!/bin/bash FILES="/home/john/my directory/*.txt" for f in "${FILES}" do echo "${f}" done 

Haluan vain luetella kaikki .txt -tiedostot for -silmukassa, jotta voin tehdä juttuja heidän kanssaan. Mutta tilaa my directory ja tähti kohdassa *.txt eivät vain pelaa hienosti. Yritin käyttää sitä lainausmerkkien kanssa ja ilman niitä, sekä muuttujien nimissä olevilla kaarevilla aaltosulkeilla, mutta en silti voi ”tulostaa kaikkia .txt -tiedostoja.

Tämä on hyvin yksinkertainen asia, mutta en vielä kamppaile, koska en ole väsynyt ja en voi ajatella suoraan.

Mitä teen väärin?

Olen onnistunut hakemaan yllä oleva komentosarja, jos TIEDOSTOISIIN ei ole välilyöntiä tai tähtiä … Minun oli kokeiltava kaksoislainausmerkkien ja aaltosulkeiden käyttöä saadakseni sen toimimaan. Mutta kun minulla on sekä välilyöntejä että tähti, se sekoittaa kaiken.

Vastaa

Lainausmerkkien sisällä * ei laajene tiedostoluetteloon. Jotta tällaista jokerimerkkiä voidaan käyttää onnistuneesti, sen on oltava lainausmerkkien ulkopuolella.

Vaikka jokerimerkki laajenisi, lauseke "${FILES}" johtaisi yhteen merkkijonoon, ei luettelo tiedostoista.

Yksi toimiva lähestymistapa olisi:

#!/bin/bash DIR="/home/john/my directory/" for f in "$DIR"/*.txt do echo "${f}" done 

Edellä tiedostojen nimet, joissa on välilyöntejä tai muita vaikeita merkkejä käsitellään oikein.

Edistyneempi lähestymistapa voi käyttää bash-taulukoita:

#!/bin/bash FILES=("/home/john/my directory/"*.txt) for f in "${FILES[@]}" do echo "${f}" done 

Tällöin FILES on joukko tiedostojen nimiä. Määritelmän ympärillä olevat parenit tekevät siitä matriisin. Huomaa, että * on lainausmerkkien ulkopuolella. Rakenne "${FILES[@]}" on erikoistapaus: se laajenee merkkijonoluetteloon, jossa kukin merkkijono on yksi tiedostonimistä. Tiedostojen nimet, joissa on välilyöntejä tai muita vaikeita merkkejä, käsitellään oikein.

Kommentit

  • hyvä, että toimi
  • Se ’ on syytä huomata, että jos ’ välität tämänkaltaisia polkuja toimintojen läpi, sinun on varmistettava, että mainitset muuttujan yksin sen sijaan, että liität sen osaksi suurempaa merkkijonoa: for f in "$DIR"/*.txt = fine for f in "$DIR/*.txt" = katkeaa

Vastaa

Vaikka John1024: n osoittamien taulukoiden käyttäminen on paljon järkevämpää, tässä voit käyttää myös split + glob -operaattoria (poistuminen skalaarimuuttuja, jota ei ole mainittu).

Koska haluat vain kyseisen operaattorin globaalin osan, sinun on poistettava käytöstä split -osa:

#! /bin/sh - # that also works in any sh, so you don"t even need to have or use bash file_pattern="/home/john/my directory/*.txt" # all uppercase variables should be reserved for environment variables IFS="" # disable splitting for f in $file_pattern # here we"re not quoting the variable so # we"re invoking the split+glob operator. do printf "%s\n" "$f" # avoid the non-reliable, non-portable "echo" done 

Vastaa

Voit tehdä vain jättää vain jokerimerkit lainausmerkkien ulkopuolelle.
Jotain:
for a ”tiedostoissa, joissa on välilyöntejä” * ”. txt ”
tee
käsittely
tehty

Jos jokerit itse laajenevat välilyönteihin, sinun ei tarvitse käyttää tiedostoa rivikohtaisesti, kuten käyttää ls -l tiedostoluettelon luomiseen ja käytä bash read -ohjelmaa saadaksesi kukin tiedosto.

Vastaus

Yksirivinen ratkaisu (suoritettavaksi Terminalissa):
/usr/bin/find "./" -not -type d -maxdepth 1 -iname "*.txt" -print0 | while IFS= read -r -d $"\0" f ; do { echo "${f}"; }; done; unset f;

tapauksessasi / OP, vaihda "./" muotoon "/home/john/my directory/"

Käytä komentotiedostossa:

 #!/bin/bash /usr/bin/find "./" -not -type d -maxdepth 1 -iname "*.txt" -print0 | while IFS= read -r -d $"\0" f ; do { echo "${f}"; # your other commands/codes, etc }; done; unset f;  

Yllä olevat toiminnot voidaan saavuttaa myös tällä (suositellulla) tavalla:

 #!/bin/bash while IFS= read -r -d $"\0" f ; do { echo "${f}"; # your other commands/codes, etc }; done < <(/usr/bin/find "./" -not -type d -maxdepth 1 -iname "*.txt" -print0); unset f;  

Lyhyt / lyhyt KUVAUS:

"./": tämä on nykyinen hakemisto. määritä hakemistopolku.
-not -type d: tässä -not määrittää sen ohittamaan seuraavaksi mainittu tyyppi, & seuraavaksi mainittu -type on d = hakemistot, joten se ohittaa hakemistot. Käytä tiedostojen ohittamiseen f -kohtaa d -kohdan sijaan. Käytä l d -kohdan sijaan ohittaa symlink-tiedostot.
-maxdepth 1: määrittää sen etsimään tiedostoja vain nykyiseltä (alias: yksi) hakemistotasolta. Jos haluat löytää tiedoston jokaisen & ensimmäisen alihakemistotason sisällä, aseta maxdepth arvoksi 2. Jos -maxdepth ei käytetä, se etsii rekursiivisesti (alihakemistojen sisällä) jne.
-iname "*.jpg": tässä -iname määrittää sen etsimään tiedostoja ja ohittamaan (ylempi / alempi) -kotelo tiedostonimessä / laajennuksessa. -name ei ohita kirjainta. -lname löytää symboleita. jne.
-print0: se tulostaa nykyisen tiedoston polun vakiolähdöksi, jota seuraa ASCII NUL -merkki (merkkikoodi 0), jonka me tunnista myöhemmin käyttämällä read -kohtaa while -palvelussa.
IFS=: tässä sitä käytetään, jos tiedostonimi päättyy välilyöntiin. Käytämme NUL / "" / \0 IFS: n kanssa jokaisen löydetyn tiedostonimen havaitsemiseksi. Koska ” etsi ” on määritetty erottamaan ne \0 jonka tuottaa -print0.
read -r -d $"\0" fileName: $"\0" on "" / NUL. -r -merkkiä käytettiin siinä tapauksessa, että tiedoston nimessä on takaviiva.
read [-ers] [-a aname] [-d rajaa] [-i teksti] [-n nchars] [-N nchars] [-p kehote] [-t aikakatkaisu] [-u fd] [nimi. ..] […]
-r Backslash ei toimi pakomerkkinä. Takasuuntaisen viivan katsotaan olevan osa viivaa. Etenkin jälkiviivan ja uuden rivin paria ei saa käyttää viivan jatkoa.
done < <(...): Prosessin korvaaminen, jota käytetään tässä lähetettäessä / putken ulostulo ” etsi ” osaan ” luettu ”

, kun taas ” -loop. Lisätietoja: https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html, https://wiki.bash-hackers.org/syntax/expansion/proc_subst, https://tldp.org/LDP/abs/html/abs-guide.html#PROCESS-SUB, https://tldp.org/LDP/abs/html/abs-guide.html#PSUBP


Toisessa @ John1024: n vastauksessa hän on osoittanut upean bash-pohjaisen ratkaisun, joka ei käytä ” etsi ”, ulkoinen apuohjelma.
” etsi ” on erittäin tehokas & nopea, pidän siitä mieluummin, kun käsiteltäviä tiedostoja on liian monta.

@ John1024 ”-ratkaisussa se tulostaa matching-säännön rivi, kun hakemistossa ei ole tiedostoa, joten ohittaa alla olevaa riviä [ ! -e "${f}" ]...,
tässä on yksirivinen ratkaisu, jota käytetään suoraan Terminalissa:
DIR="/home/john/my directory/" ; for f in "$DIR"*.txt ; do { [ ! -e "${f}" ] && continue; echo "${f}"; }; done; unset DIR;

Tässä on komentosarja:

 #!/bin/bash DIR="/home/john/my directory/"; for f in "$DIR"*.txt ; do { [ ! -e "${f}" ] && continue; echo "${f}"; # your codes/commands, etc }; done; unset DIR;  

Huomaa: jos DIR-hakemistossa on "/" kauttaviiva (hakemiston ilmaisin) lopussa, sitten matching-säännössä , uudelleen "/" -ominaisuuden käyttäminen ei ole välttämätöntä,
Tai tee päinvastoin: DIR: ssä älä käytä "/" -kohtaa ja käytä sitä siis matching-säännössä "$DIR"/*.txt


Tämä ylimääräinen tarkistus käyttämällä [ ! -e "${f}" ]... -koodia voidaan välttää, jos alle shell-option (alias: ” shopt ”) käytetään tai otetaan käyttöön:
shopt -s nullglob

Jos komentosarja muutti shopt-tilan, se aiheuttaa muissa bash-pohjaisissa komento-ohjelmissa odottamattomia / odottamattomia ongelmia.

Jotta käyttäytyminen olisi johdonmukaista kaikissa skripteissä, jotka käyttävät bash, bash-shell-option tila tulisi tallentaa / tallentaa komentosarjaasi, ja kun olet käyttänyt komentosarjan ensisijaisia toimintoja, kyseinen shell-vaihtoehto olisi palautettava aiempiin asetuksiin.

Käytämme backtickiä (alias: grave-accent, alias: backquote) `...` komentojen korvaaminen (sisäisille bash-komentokoodeille jne.), jotta ei synny uutta alikuorta, säilyttää käänteisen viivan kirjaimellinen merkitys laajempaa tukea varten (alias: siirrettävyys) jne., Backtick-pohjaisina sisäisinä bash-komentoina jne. voidaan usein suorittaa samassa kuoressa kuin komentosarja jne., Ja niin se on vähän nopeampi & parempi ja myös paremmin siihen tarkoitukseen, jota käsittelemme täällä. Jos haluat mieluummin $(...) komennon korvaamisen, käytä sitä, kenelläkään on vapaus valita & oikeus valita haluamansa, välttämätön jne. Lisätietoja : täällä .

Joten yllä oleva komentosarja tulee jälleen näkyviin, & tällä kertaa shoptin edelliset asetukset palautettu ennen komentosarjan lopettamista:

 #!/bin/bash DIR="/home/john/my directory/"; ub="/usr/bin"; # shopt = shell-option(s). # Setting-up "previous-nullglob" state to "enabled"/"on"/"1": p_nullglob=1; # The "shopt -s" command output shows list of enabled shopt list, so if # nullglob is NOT set to ON/enabled, then setting "previous_nullglob" as 0 [ "`shopt -s | ${ub}/grep nullglob`" == "" ] && p_nullglob=0; # Enabling shell-options "nullglob": shopt -s nullglob; # previous code, but without the extra checking [ ! -e "${f}" ]... line: for f in "$DIR"*.txt ; do { echo "${f}"; # your codes/commands, etc }; done; # As we have utilized enabled nullglob shopt, its now in enabled state, # so if previously it was disabled only-then we will disable it: [ "$p_nullglob" -eq "0" ] && shopt -u nullglob; unset DIR; unset p_nullglob ub;  

shopt -p shoptName -tulos (esimerkiksi: shopt -p dotglob) voi olla joko
, tämä: shopt -u shoptName (u on unset / pois päältä / pois / 0)
tai tämä: shopt -s shoptName ( s on set / käytössä / päällä / 1)
sijainti kirjaimen "s" tai "u" kirjain on aina kohdassa 7 (koska bashissa merkkijono ”s kirjaimen sijainti alkaa 0, ts. merkkijonon ensimmäinen kirjain on paikassa 0)
Me voi saada tämän "u" tai ja tallenna se muuttujaan, jotta voimme käyttää sitä palauttamaan edellisen tilan.
Ja jos käytämme tätä (yllä mainittua) tapaa tallentaa / palauttaa shopt-tila, voimme vältä ulkoisen työkalun "grep" käyttöä.

Voit tarkastella "txt" -tiedostoa, joka alkaa "." eli piilotetun "txt" -tiedoston tarkastelemiseksi meidän on otettava käyttöön "dotglob" shopt.

Joten tällä kertaa alla olevaan "dotglob" sisältyy &, joka näyttää piilotetut "txt" -tiedostot:

 #!/bin/bash DIR="/home/john/my directory/"; p_nullglob="u"; pSS="`shopt -p nullglob`"; [ "${pSS:7:1}" == "s" ] && p_nullglob="s"; p_dotglob="u"; pSS="`shopt -p dotglob`"; [ "${pSS:7:1}" == "s" ] && p_dotglob="s"; shopt -s nullglob dotglob; for f in "$DIR"*.txt ; do { echo "${f}"; # your codes/commands, etc }; done; [ "$p_nullglob" == "u" ] && shopt -u nullglob; [ "$p_dotglob" == "u" ] && shopt -u dotglob; unset DIR; unset p_nullglob p_dotglob pSS;  

On helpompaa tapaa tallentaa / palauttaa shopt vaihtoehto / arvo.
Isaac lähetti tänne , kuinka tallentaa + palauttaa Env / Shopt-muuttuja / option tila / arvo.

Shopt-tilan tallentaminen ” nullglob ”:

... # your primary-function codes/commands, etc lines
Palautetaan ” nullglobin edellisen shopt-tilan takaisin ”, ennen komentosarjalta poistumista:
eval "$p_nullglob" ;

Useita vaihtotiloja voidaan tallentaa tällä tavalla:
p_multipleShopt="`shopt -p nullglob dotglob`";
ja palautusprosessi on sama kuin aikaisemmin:
eval "$p_multipleShopt" ;

Tallenna KAIKKI shopt-tilat tällä tavalla:
p_allShopt="`shopt -p`";
ja palautusprosessi on sama kuin aiemmin:
eval "$p_allShopt" ;

Tässä on siis toinen bash-pohjainen ratkaisu:

 #!/bin/bash DIR="/home/john/my directory/"; p_allShopt="`shopt -p`"; shopt -s nullglob dotglob; for f in "$DIR"*.txt ; do { echo "${f}"; # your codes/commands, etc }; done; eval "$p_allShopt" ; unset DIR p_allShopt;  

Käyttämällä eval on turvallista yllä, koska muuttuja "$p_allShopt" ei pidä käyttäjän toimittamia tietoja tai tietoja, joita ei ole desinfioitu, Tämä muuttuja pitää sisällään bashin sisäisen komennon shopt.
Jos haluat silti välttää eval, käytä alla olevaa koodia niin lution:

 #!/bin/bash DIR="/home/john/my directory/"; p_allShopt="`shopt -p`"; shopt -s nullglob dotglob; for f in "$DIR"*.txt ; do { echo "${f}"; # your codes/commands, etc }; done; while IFS= read -a oneLine ; do { ${oneLine} ; }; done < <(echo "$p_allShopt") ; unset DIR p_allShopt oneLine;  

Harvat (muut) merkittävät & liittyvät SHOPT , joista voi olla hyötyä, ovat:

  • nocaseglob: Jos se on asetettu, Bash vastaa tiedostonimiä Kirjainkoon erottamaton muoto suoritettaessa tiedostonimen laajennusta.
  • nocasematch: Jos asetus on asetettu, Bash sovittaa kuviot kirjainkoon mukaan, kun suoritetaan sovitusta suoritettaessa case tai [[ ehdolliset komennot, kun suoritetaan kuvion korvaussanan laajennuksia tai suodatetaan mahdollisia täydennyksiä osana ohjelmoitavaa täydennystä.
  • dotglob: Jos asetettu, Bash sisältää tiedostonimet, jotka alkavat ‘.’ tiedostonimen laajennuksen tuloksissa. Tiedostonimet ‘.’ ja ‘..’ on aina sovitettava nimenomaisesti, vaikka dotglob on asetettu.
  • nullglob: Jos asetettu , Bash antaa tiedostonimikuvioiden, jotka eivät täsmää tiedostoja, laajenevan nolla merkkijonoon pikemminkin kuin itsensä.
  • extglob: Jos asetus on asetettu, yllä kuvatut laajennetut kuvion vastaavuudet (katso Kuvion haku ) ovat käytössä.
  • globstar: Jos asetus on asetettu, tiedostonimen laajennuskontekstissa käytetty malli ‘**’ vastaa kaikkia tiedostoja ja nolla tai useampia hakemistoja ja alihakemistoja. Jos mallia seuraa ‘/’, vain hakemistot ja alihakemistot vastaavat toisiaan.

Vastaa

Kun haluat käsitellä tiedostoja, voit ajatella, että niiden nimessä voi olla välilyönti tai muu komentokoodi tulee olemaan, joten ennen kuin aloitat prosessisi, kuten for loop tai find command aseta IFS bash env variable -asetukseksi:

IFS=$(echo -en "\n\b") 

Vastaa

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