$ ls -l /tmp/test/my\ dir/ total 0
Kíváncsi voltam, miért nem sikerül vagy sikerül a fenti parancs futtatásának következő módjai?
$ abc="ls -l "/tmp/test/my dir"" $ $abc ls: cannot access ""/tmp/test/my": No such file or directory ls: cannot access "dir"": No such file or directory $ "$abc" bash: ls -l "/tmp/test/my dir": No such file or directory $ bash -c $abc "my dir" $ bash -c "$abc" total 0 $ eval $abc total 0 $ eval "$abc" total 0
Megjegyzések
- mywiki.wooledge.org/BashFAQ / 050
- Biztonsági következmények, ha elfelejtünk idézni egy változót a bash / POSIX héjakban – De mi van, ha…?
Válasz
Ezt az unix.SE-n számos kérdés megvitatta, megpróbálom összegyűjteni az összes kérdések, amelyeket itt fel tudok találni. Hivatkozások a végén.
Miért nem sikerül
Az oka annak, hogy szembesül a problémákkal: szófelosztás és az a tény, hogy a változókból kibővített idézetek nem idézőjelekként működnek, hanem csak hétköznapi karakterek.
a kérdésben bemutatott esetek:
Az itt megadott hozzárendelés egyetlen sztringet rendel hozzá ls -l "/tmp/test/my dir"
– abc
:
$ abc="ls -l "/tmp/test/my dir""
Lent, $abc
fel van osztva a szóközzel, és ls
megkapja a három argumentumot -l
, "/tmp/test/my
és dir"
(a második elején egy idézettel, a harmadik hátulján pedig egy másikkal). Az opció működik, de az útvonal helytelenül kerül feldolgozásra:
$ $abc ls: cannot access ""/tmp/test/my": No such file or directory ls: cannot access "dir"": No such file or directory
Itt a bővítmény idézett, tehát egyetlen szóként marad. A shell megpróbálja hogy megtalálja a szó szoros értelmében ls -l "/tmp/test/my dir"
nevű programot, szóközökkel és idézőjelekkel.
$ "$abc" bash: ls -l "/tmp/test/my dir": No such file or directory
És itt: $abc
fel van osztva, és csak az első eredményül kapott szót vesszük argumentumként a -c
argumentumnak, így Bash csak futtatja a ls
az aktuális könyvtárban. A többi szó a bash argumentuma, és a $0
, $1
stb.
$ bash -c $abc "my dir"
bash -c "$abc"
és eval "$abc"
esetén további egy van shell feldolgozási lépés, amely az idézőjeleket működőképessé teszi, de emellett az összes shell kiterjesztést újra feldolgozza , így fennáll annak a veszélye, hogy véletlenül futtatunk például egy parancscserét a felhasználó által megadott adatokból, hacsak nem ön ” nagyon vigyázok az idézésről.
Jobb módszerek rá
A parancs kétféle jobb tárolási módja: a) használjon inkább egy függvényt, b) használjon egy tömb változót ( vagy a helyzeti paraméterek).
Funkció használata:
Egyszerűen deklarálja egy függvényt, amelynek belsejében van a parancs, és futtassa a függvényt, mintha parancs lenne. A függvényen belüli parancsok kiterjesztéseit csak akkor dolgozzuk fel, amikor a parancs fut, és nem akkor, amikor meg van határozva, és nem kell idéznie az egyes parancsokat.
# define it myls() { ls -l "/tmp/test/my dir" } # run it myls
Tömb használata:
A tömbök lehetővé teszik többszavas változók létrehozását, ahol az egyes szavak fehéret tartalmaznak tér. Itt az egyes szavakat külön tömbelemekként tárolják, és a "${array[@]}"
kiterjesztés az egyes elemeket külön shell szavakként bővíti ki:
# define the array mycmd=(ls -l "/tmp/test/my dir") # run the command "${mycmd[@]}"
A szintaxis kissé szörnyű, de a tömbök lehetővé teszik a parancssor egyesével történő felépítését is. Például:
mycmd=(ls) # initial command if [ "$want_detail" = 1 ]; then mycmd+=(-l) # optional flag fi mycmd+=("$targetdir") # the filename "${mycmd[@]}"
vagy tartsa állandóan a parancssor egyes részeit, és a tömb csak egy részét töltse ki, például opciókat vagy fájlneveket:
options=(-x -v) files=(file1 "file name with whitespace") target=/somedir transmutate "${options[@]}" "${files[@]}" "$target"
A tömbök hátránya, hogy “nem szabványos funkciók, ezért a sima POSIX-héjak (például dash
, az alapértelmezett) /bin/sh
a Debian / Ubuntuban) nem támogatja őket (de lásd alább). A Bash, a ksh és a zsh azonban igen, ezért valószínűleg a rendszernek van olyan héja, amely támogatja a tömböket.
A
Azokban a héjakban, amelyek nem támogatják a megnevezett tömböket, továbbra is használhatók a helyzeti paraméterek (az áltömb "$@"
) a parancs argumentumainak megtartásához.
A következőknek olyan hordozható szkriptbiteknek kell lenniük, amelyek megegyeznek az előző szakasz kódbitjeivel. A tömböt "$@"
, a helyzeti paraméterek listája. A "$@"
beállítása a set
és a dupla idézőjelekkel történik. a "$@"
körül fontos (ezek a lista elemeit külön-külön idézik).
Először egyszerűen argumentumokat tartalmazó parancsot kell tárolni a "$@"
fájlban és futtatni:
set -- ls -l "/tmp/test/my dir" "$@"
A parancs parancssori opcióinak feltételes beállítása:
set -- ls if [ "$want_detail" = 1 ]; then set -- "$@" -l fi set -- "$@" "$targetdir" "$@"
Csak a "$@"
parancsot használja opciókhoz és operandusokhoz :
set -- -x -v set -- "$@" file1 "file name with whitespace" set -- "$@" /somedir transmutate "$@"
(Természetesen a "$@"
általában magának a szkriptnek az argumentumaival van kitöltve, tehát Ön ” El kell mentenem őket valahová, mielőtt újratervezném a "$@"
.)
Legyen óvatos a eval
!
Mivel a eval
egy további szintet vezet be az árajánlatok és a bővítés feldolgozásában, körültekintően kell eljárnia a felhasználói bevitelsel. Például ez addig működik, amíg a felhasználó nem ír be egyetlen idézőjelet sem:
read -r filename cmd="ls -l "$filename"" eval "$cmd";
De ha megadják a bemenetet "$(uname)".txt
, akkor a szkript boldogan futtatja a parancs behelyettesítését.
A tömbökkel ellátott verzió immúnis a th-re mivel mivel a szavakat egész idő alatt külön tartják, nincs árajánlat vagy egyéb feldolgozás a filename
tartalmához.
read -r filename cmd=(ls -ld -- "$filename") "${cmd[@]}"
Hivatkozások
- Szó felosztás a BashGuide
- BashFAQ / 050 vagy ” Parancsot próbálok tenni változóban, de az összetett esetek mindig kudarcot vallanak! ”
- A kérdés a shell szkriptem elfojtja a szóközöket vagy más speciális karaktereket? , amelyek számos kérdést tárgyalnak az idézéssel és a szóközzel, beleértve a parancsok tárolását is.
Megjegyzések
- megkerülheti az eval idéző dolgot a
cmd="ls -l $(printf "%q" "$filename")"
művelettel. nem szép, de ha a felhasználó halottan van beállítva egyeval
használatával, az segít. ‘ nagyon hasznos a parancs elküldéséhez hasonló dolgokon keresztül is, példáulssh foohost "ls -l $(printf "%q" "$filename")"
, vagy ennek a kérdésnek az írása alatt:ssh foohost "$cmd"
. - Nem közvetlenül kapcsolódik, de keményen kódolta a könyvtárat? Ebben az esetben érdemes álnevet nézni. Valami ilyesmi:
$ alias abc='ls -l "/tmp/test/my dir"'
Válasz
A legbiztonságosabb módszer futtasson egy (nem triviális) parancsot: eval
. Ezután a parancsot úgy írhatja, ahogyan a parancssorra tenné, és pontosan úgy hajtódik végre, mintha csak beírta volna. De mindent idéznie kell.
Egyszerű eset:
abc="ls -l "/tmp/test/my dir"" eval "$abc"
nem olyan egyszerű eset:
# command: awk "! a[$0]++ { print "foo: " $0; }" inputfile abc="awk "\""! a[$0]++ { print "foo: " $0; }"\"" inputfile" eval "$abc"
Megjegyzések
- Szia @HaukeLaging, mi az a
- @jumping_monkey
'
nem lehet'
-jelölt karaktersorozaton belül . Így (1) be kell fejeznie / meg kell szakítania az idézetet, (2) el kell kerülnie a következő'
et annak érdekében, hogy szó szerint megkapja, (3) írja be a szó szerinti idézetet és (4) megszakítás esetén folytassa az idézett karakterláncot:(1)'(2)\(3)'(4)'
- Szia @HaukeLaging, érdekes, és miért nem csak
abc='awk \'! a[$0]++ { print "foo: " $0; }\' inputfile'
? - @jumping_monkey Nem is beszélve arról, hogy csak elmagyaráztam neked, hogy ez miért nem működik: Nem lenne ‘ értelme tesztelni a kódot, mielőtt elküldenéd?
Válasz
A második idézőjel megszakítja a parancsot.
Amikor futtatom:
abc="ls -l "/home/wattana/Desktop"" $abc
Hibát adott.
De amikor futok
abc="ls -l /home/wattana/Desktop" $abc
Egyáltalán nincs hiba
Ezt egyelőre nem lehet kijavítani (számomra), de elkerülheti a hibát, ha nincs hely a könyvtárnévben.
Ez a válasz azt mondta, hogy az eval paranccsal lehet ezt kijavítani, de nekem nem működik 🙁
megjegyzések
- Igen, ez addig működik, amíg ‘ nincs szükség pl. fájlnevek beágyazott szóközökkel (vagy globális karaktereket tartalmazóakkal).
Válasz
Ha ez nem működik a ""
, akkor használja a ``
:
abc=`ls -l /tmp/test/my\ dir/`
Frissítés jobb mód:
abc=$(ls -l /tmp/test/my\ dir/)
Megjegyzések
- Ez a parancs eredményét egy változóban tárolja. Az OP (furcsa módon) magát a parancsot akarja egy változóba menteni. Ja, és valóban el kell kezdened használni a
$( command... )
-t a háttérbillentyűk helyett. - Nagyon köszönöm a pontosításokat és tippeket!
Válasz
Mit szólnál egy python3 egysoroshoz?
bash-4.4# pwd /home bash-4.4# CUSTOM="ls -altr" bash-4.4# python3 -c "import subprocess; subprocess.call(\"$CUSTOM\", shell=True)" total 8 drwxr-xr-x 2 root root 4096 Mar 4 2019 . drwxr-xr-x 1 root root 4096 Nov 27 16:42 ..
Megjegyzések
- A kérdés a BASH-ról szól, nem a pythonról.
Válasz
Egy másik egyszerű trükk a abc
változó:
$ history -s $abc
és nyomja meg a UpArrow
vagy Ctrl-p
parancsot. Minden más módszerrel ellentétben, így szükség esetén a végrehajtás előtt szerkesztheti.
Ez a parancs a változó tartalmat új bejegyzésként fűzi hozzá a Bash előzményekhez, és a UpArrow
.