Bash script hiba olyan karakterláncokkal, amelyek útvonalai szóközökkel és helyettesítő karakterekkel rendelkeznek

Problémáim vannak a Bash parancsfájlok alapjainak levezetésével. Itt van eddig:

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

Csak annyit szeretnék tenni, hogy felsorolom az összes .txt fájlt egy for ciklusban, így tudok velük dolgokat csinálni. De a my directory mező és a csak nem játszanak szépen. Próbáltam dupla idézőjelekkel és anélkül használni, göndör zárójelekkel és anélkül a változóneveken, és még mindig nem tudom kinyomtatni az összes .txt fájlt.

Ez egy nagyon alapvető dolog, de még mindig küzdök, mert nem vagyok fáradt és nem tudok egyenesen gondolkodni.

Mit csinálok rosszul?

Sikeresen tudtam jelentkezni a fenti szkript, ha a FÁJLJAIMON nincs szó vagy csillag … Kettős idézőjelek és zárójelek használatával vagy anélkül kellett kísérleteznem, hogy működjön. De abban a pillanatban, hogy van szóközöm és csillagom, ez mindent elront.

Válasz

Idézetek között a * nem bővül a fájlok listájára. Az ilyen helyettesítő karakter sikeres használatához az idézőjeleken kívül kell lennie.

Még ha a helyettesítő karakter is kibővült, a "${FILES}" kifejezés egyetlen karakterláncot eredményez, nem pedig fájlok listája.

Az egyik megközelítés működőképes lehet:

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

A fentiekben szóközökkel vagy más nehézséggel rendelkező fájlnevek a karaktereket megfelelően kezeljük.

Egy fejlettebb megközelítés használhatja a bash tömböket:

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

Ebben az esetben a fájlnevek tömbje. A definíciót körülvevő parének tömbvé teszik. Ne feledje, hogy az * árajánlatokon kívül van. A "${FILES[@]}" konstrukció egy különleges eset: kibővül egy karaktersorozatok listájára, ahol minden karaktersorozat a fájlnevek egyike. A szóközökkel vagy más nehéz karakterekkel rendelkező fájlneveket helyesen kezeljük.

Megjegyzések

  • nagyszerűek, amelyek működtek
  • Ez ‘ érdemes megjegyezni, hogy ha ‘ átengeded az ilyen utakat a függvényeken keresztül, akkor biztosan idézned kell a változót önmagában ahelyett, hogy összefűzné egy nagyobb karaktersorozat részeként: for f in "$DIR"/*.txt = fine for f in "$DIR/*.txt" = szakít

Válasz

Míg a John1024 által bemutatott tömbök használata sokkal értelmesebb, itt a split + glob operátort is használhatja (elhagyva egy skaláris változó nem idézett).

Mivel csak annak az operátornak a globális részét szeretné, le kell tiltania a split részt:

#! /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 

Válasz

Megteheti, hogy csak a helyettesítő karaktereket hagyja idézőjelek nélkül.
Valami ilyesmi:
for a “szóközökkel rendelkező fájlokban” * “. txt “

feldolgozás
kész
Ha a helyettesítő karakterek maguk is kibővülnek szóközökre, akkor nem kell egy sort használnia egy fájlt, mint például az ls -l használatával létrehozni a fájlok listáját, és használja a bash read parancsot az egyes fájlok megszerzéséhez.

Válasz

Egysoros megoldás, (a Terminálban történő futtatáshoz):
/usr/bin/find "./" -not -type d -maxdepth 1 -iname "*.txt" -print0 | while IFS= read -r -d $"\0" f ; do { echo "${f}"; }; done; unset f;

az Ön / OP esetére módosítsa a "./" szöveget

Szkriptfájlban való használat:

 #!/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;  

A fenti funkcionalitás ezen (ajánlott) módon is elérhető:

 #!/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;  

Rövid / rövid LEÍRÁS:

"./": ez az aktuális könyvtár. adjon meg egy könyvtár elérési utat.
-not -type d: itt a -not úgy konfigurálja, hogy átugorja a legközelebb említett típus, & következő -type a d = könyvtárak, tehát kihagyja könyvtárakat. Használja a f fájlt a d helyett a fájlok kihagyásához. Használja a l elemet a d helyett a symlink-fájlok kihagyásához.
-maxdepth 1: úgy állítja be, hogy csak a jelenlegi (más néven: egy) könyvtárszintű fájlokra találjon. Ha a fájlokat az & első alkönyvtárszinteken belül találja meg, állítsa a maxdepth értéket 2-re. Ha a -maxdepth értéket nem használják, akkor rekurzív módon fog keresni (alkönyvtárak belsejében) stb.
-iname "*.jpg": itt a -iname konfigurálja fájlok keresésére és figyelmen kívül hagyására (felső / alsó) tok a fájlnévben / kiterjesztésben. A -name nem hagyja figyelmen kívül a kis- és nagybetűket. A -lname szimbolikus linkeket talál. stb.
-print0: az aktuális fájl elérési útját kinyomtatja a szabványos kimenetre, majd egy ASCII NUL karaktert (0 karakterkód) követ, amelyet későbbi észlelés a read használatával a while fájlban.
IFS=: itt használatos, ha egy fájlnév szóközzel végződik. A NUL / "" / \0 -t IFS-sel együtt használjuk az egyes talált fájlnevek felderítésére. Mivel ” keresés ” úgy van beállítva, hogy elválasztja őket a \0, amelyet a -print0 állít elő.
read -r -d $"\0" fileName: a $"\0" "" / NUL. A -r -et arra az esetre használták, ha egy fájlnévnek hátlapja van.
read [-ers] [-a aname] [-d elválaszt] [-i szöveg] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [név. ..] […]
-r A Backslash nem menekülési karakterként működik. A visszavágás a vonal részének számít. Különösen a visszavonat-újsor pár nem használható sorfolytatásként.
done < <(...): Az itt küldött folyamat-helyettesítés / a ” cső kimenete a ” megtalálásához a ” beolvasáshoz iv id = “329cb662fc” “>

, míg ” -loop. További információ: 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


A @ John1024 másik válaszában remek bash alapú megoldást mutatott be, amely nem használja a ” find “, egy külső segédprogram.
” find ” nagyon hatékony & gyors, jobban szeretem, ha túl sok fájl kezelhető.

a @ John1024 “megoldásában kinyomtatja a matching-rule sor, ha nincs fájl a könyvtárban, ezért az [ ! -e "${f}" ]... sor alatt ennek kihagyására szolgál,
itt egysoros megoldás használható közvetlenül a Terminálban:
DIR="/home/john/my directory/" ; for f in "$DIR"*.txt ; do { [ ! -e "${f}" ] && continue; echo "${f}"; }; done; unset DIR;

Íme egy szkript:

 #!/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;  

Megjegyzés: ha a DIR könyvtárában "/" perjel (könyvtárjelző) van a végén, akkor a matching-szabályban , ismét nem szükséges a "/" használata,
Vagy tegye az ellenkezőjét: a DIR-ben ne használja a "/" -t és így használja a megfelelő szabályban "$DIR"/*.txt


Ez az extra ellenőrzés a [ ! -e "${f}" ]... kód elkerülhető, ha a shell-option (más néven: ” shopt “) használatos vagy engedélyezett:
shopt -s nullglob

Ha egy shopt állapotot megváltoztatott egy szkript, akkor egy másik bash alapú szkript programban váratlan / nem várt problémákat okoz.

Következetes viselkedés minden szkriptben, amely A bash, bash-shell-option állapotát rögzíteni kell / el kell menteni a szkriptbe, és miután elvégezte az elsődleges funkciókat a szkriptben, akkor a shell-opciót vissza kell állítani a korábbi beállításokra.

A backtick (aka: grave-accent, más néven: backquote) `...` parancs-helyettesítés (belső bash-parancs kódokhoz stb.), hogy ne szüljön új alhéj, megtartja a hátsó perjel szó szerinti jelentését, szélesebb körű támogatás (más néven: hordozhatóság) stb. miatt, mint backtick alapú belső bash-parancsok stb., gyakran végrehajthatók ugyanabban a héjban, mint a szkript stb. És így kicsit gyorsabb & jobb, és jobb is arra a célra, amelyet itt kezelünk. Ha a $(...) parancs-helyettesítést részesíti előnyben, akkor ezt használja, bárkinek joga van & jogot választani, mit szeretne, kerülni stb. További információ : itt .

Tehát a fenti szkript ismét megjelenik, & ezúttal a shopt korábbi beállításainak visszaállításával, a szkript befejezése előtt:

 #!/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;  

A shopt -p shoptName kimenete (például: shopt -p dotglob) is lehet
, ez: shopt -u shoptName (a u értéke unset / tiltva / ki / 0)
vagy ez: shopt -s shoptName ( a s set / engedélyezve / be / 1)
A pozíció a "s" vagy "u" betű mindig (mert bash-ban A string karakterláncának betűhelye 0 -től kezdődik, vagyis a karakterlánc első betűje a 0) pozícióban van.
Mi megszerezheti ezt "u" vagy és tárolja egy változóban, hogy felhasználhassuk az előző állapot visszaállítására.
És ha ezt a (fent említett) módszert alkalmazzuk a shopt állapot mentésére / helyreállítására, akkor kerülje a "grep" eszköz használatát.

A "txt" ".", vagyis a rejtett "txt" fájl megtekintéséhez engedélyeznünk kell a "dotglob" shopt.

Tehát ezúttal az alábbiakban "dotglob" szerepel &, amely lehetővé teszi a rejtett "txt" fájlok megjelenítését:

 #!/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;  

A shopt mentésének / visszaállításának egyszerűbb módjai vannak opció / érték.
Isaac ide tette közzé , hogyan mentse + állítsa vissza az Env / Shopt változó / opció állapota / érték.

A (z) ” nullglob “: shopt állapot mentése:

... # your primary-function codes/commands, etc lines
A ” nullglob korábbi shopt állapotának visszaállítása “, mielőtt kilépne a szkriptből:
eval "$p_nullglob" ;

Több shopt állapot menthető így:
p_multipleShopt="`shopt -p nullglob dotglob`";
és a visszaállítási folyamat ugyanaz, mint korábban:
eval "$p_multipleShopt" ;

MINDEN shopt állapot mentése így történik:
p_allShopt="`shopt -p`";
és a visszaállítási folyamat megegyezik az előzőekkel:
eval "$p_allShopt" ;

Tehát itt van egy másik bash alapú megoldás:

 #!/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;  

A eval használata fentről biztonságos, mivel a "$p_allShopt" változó nem rendelkezik a felhasználó által megadott adatokkal, vagy olyan adatokkal, amelyek nem megtisztítva, Ez a var a bash belső parancs kimenetét tartja shopt.
Ha továbbra is el akarja kerülni a eval -t, akkor használja az alábbiakat, így lúció:

 #!/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;  

Kevés (egyéb) figyelemre méltó & kapcsolatos SHOPT , amelyek hasznosak lehetnek, a következők:

  • nocaseglob: Ha be van állítva, a Bash egyezik a fájlnevekkel kis- és nagybetűk nem érzékenyek a fájlnév-kiterjesztés végrehajtásakor.
  • nocasematch: Ha be van állítva, Bash kis- és nagybetűk nélkül illeszti össze a mintákat, amikor case vagy [[ feltételes parancsok, mintahelyettesítő szóbővítések végrehajtásakor, vagy a lehetséges kiegészítések szűrésekor a programozható befejezés részeként.
  • dotglob: Ha be van állítva, a Bash ‘.’ a fájlnév kiterjesztésének eredményeiben. A ‘.’ és a ‘..’ fájlneveknek mindig pontosan meg kell egyezniük, még akkor is, ha a dotglob be van állítva.
  • nullglob: Ha be van állítva , A Bash lehetővé teszi, hogy a fájlokkal nem egyező fájlnévminták önmaguk helyett null karakterlánccá bővülhessenek.
  • extglob: Ha be van állítva, akkor a fent leírt kiterjesztett mintaillesztési funkciók (lásd: Mintaillesztés ) engedélyezve vannak.
  • globstar: Ha be van állítva, akkor a fájlnévkiterjesztési környezetben használt ‘**’ minta minden fájlhoz illeszkedik, és nulla vagy több könyvtár és alkönyvtár. Ha a mintát egy ‘/’ követi, akkor csak a könyvtárak és az alkönyvtárak felelnek meg.

Válasz

Ha fájlkészleten szeretne feldolgozni, akkor fontolóra veheti, hogy a nevükben szóköz lehet, vagy más scape kód lesz, ezért mielőtt elkezdené a folyamatot, például for loop vagy find command állítsa a IFS bash env variable értéket:

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

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük