Shell python barátoknak: ipython3

$ sudo apt install ipython3
$ echo /bin/ipython3 | sudo tee -a /etc/shells  # írjuk hozzá a valid shell-ek listájához
$ chsh                                                           # átírni benne /bin/ipython3 -ra

 

És a /bin/bash le van cserélve egy olyan shellre, amely
  !ls -l
  !mc
  !cat
mindent visz ilyen felkiáltójeles módon, de innentől az a shell trükk sem okoz problémát, hogy

In [1]: import json

In [2]: a = json.loads('{"alma": "piros", "körte": "zöld"}')  # vagy json.load(filep)

In [3]: print (a['körte'])
zöld

In [4]: !echo $a
{alma: piros, körte: zöld}

In [5]: b = a['alma']

In [6]: !echo $b
piros

Nem tudom, mikor lesz ez jó és mikor nem, mindenesetre érdekes színfoltot hozott, így egyelőre próbaként lecseréltem erre az alapértelmezett shell-t.

Hozzászólások

Épp most kell Python kódot migrálnom Java-ra. Hát nem nyerte el a Python a tetszésem.

Mondjuk pozitív, mert kicsit eljátszogattam a jython-nal, hogy ugyanazokkal a tesztekkel a Python és a Java implementációt is tudjam tesztelni, az egész érdekes dolog volt.

Mindenesetre sok sikert a shell-hez, az is érdekesnek néz ki.

Ez igazan akkor lenne jo, ha a szokasos shell parancsokat (azok megfelelojet) el lehetne erni kenyelmesen valamilyen liben keresztul. Ilyesmire gondolok pl:

import python_shell_cmds as psc
files=psc.ls('~')
for f in files:
  psc.mv(f,f+'.old')

Sokszor kell bash-ben valamilyen parancs kimenetet parse-olni, azt meg lehetne uszni (ahogy az amugy nagyon fura powershellnel is meg van oldva, hogy objektumokat ad at).

When you tear out a man's tongue, you are not proving him a liar, you're only telling the world that you fear what he might say. -George R.R. Martin

Ez ennyit tud:

In [11]: a = !ls -l

In [12]: a[1].split()
Out[12]: 
['-rw-rw-r--',
 '1',
 'zsolt',
 'zsolt',
 '1075944251',
 'aug',
 '20',
 '14:03',
 '2020-08-20-raspios-buster-arm64.zip']

In [13]: a[1].split()[8]
Out[13]: '2020-08-20-raspios-buster-arm64.zip'

 

In [3]: files = !ls -1

In [4]: for f in files:
   ...:        !mv $f $f".old"
   ...: 

# átnevezte

... továbbra is ismerkedem vele.

A szokásos shell parancsokat erőltetett módon el lehet érni a subprocess  modul segítségével, de ha már az  [I]Pythont használja az ember, akkor  sokkal jobb a pathlib modul használata, amellyel a fájlnév-manipulációt op.rendszertől független módon végezhetjük. A pathlib modullal mindent elvégezhetünk, bár elsőre lehet, hogy kézreállóbbnak tűnik némely esetben az os- vagy az  shutils modul valamely eljárásának az alkalmazása.

Szerkesztve: 2021. 01. 26., k - 23:34

Hasonlót megnéznék Rubyhoz, valahogy sokkal alkalmasabb nyelvnek tűnik erre. Igaz pipeline operátor még nincs, bár igény volna rá.

Illetve van is ilyen, igaz a pipe-ot csak a saját típusán belül tudja.

Egy megjegyzés azok számára, akik nem otthonosak a Linuxon: az IPython "!"-prefixált parancsaihoz nem szükséges  a @hg2ecz által említett shell-csere, ezek akkor is működnek, ha csak "simán" elindítjuk az IPython-t. Windows-on természetesen a wines parancsokat adhatjuk  ki hasonlóan, például:

!powershell -command Format-Hex -Path fájlnév

Az op.rendszertől függetlenül használható pl. a %cd és a %pwd stb. (lásd a többit a helpben,  az IPythonban: %magic)

érdekes, hogy a 4-esnél az "$a"-t nem bash associative array-ra fordítja hanem valami furcsa json-de-nem-is-json formátumra (curly bracket van de double quote nincs). értem én hogy nem lehet minden python tipust bash tipusra alakítani veszteség nélkül, de mit csinál olyan python változóval amit json-ra se lehet alakítani?

jó, hát ez eléggé tetszett elsőre, de kiderült hogy gagyi...

In [2]: a = json.loads('{"a":"b","c":1}')

In [3]: !echo "$a"
{u'a': u'b', u'c': 1}

In [4]: !echo '${a}'
${ua: ub, uc: 1}

In [5]: !echo '$a'
{ua: ub, uc: 1}

most akkor exportálja a változókat hogy a shell environ-ból kapja vagy csak egy vak behelyettesítést csinál anelkül hogy teljes értékűen perse-olná a shell command line-t.

úgy látszik, hogy a második, ami viszont nem várt következményeket okozhat…

szanitizált minden python változódat, mielőtt átadnád a shellnek!

In [14]: q="1'2"

In [16]: !echo $q
sh: 1: Syntax error: Unterminated quoted string

várj, lehet hogy nem spawn-ol minden "!"-nél egy-egy új shell-t és azért nem env-en keresztül adja át a változókat, hogy minden "!" hívásnál egyetlen folytonos shell sessionben tudjon dolgozni.

In [12]: !x=5

In [13]: !echo $x

hát nem.

félig elnézést kérek, a fenti python2 volt.

python3 már valamivel konzisztensebb, de még mindig saját maga ügyetlenkedik a behelyettesítéssel:

In [3]: a = json.loads('{"a":"b","c":1}')

In [4]: !echo $a
{a: b, c: 1}

In [5]: !echo "$a"
{'a': 'b', 'c': 1}

# értem már, rendes json ez, csak single quote-okkal :)

In [6]: !echo '$a'
$a

# na, itt érti a single quote-okat

In [7]: q="1'2"

In [8]: !echo $q
/bin/bash: -c: sor: 0: váratlan EOF „'” helyett
/bin/bash: -c: sor: 1: szintaktikai hiba: váratlan fájlvége

# itt már nem


In [9]: !echo "${a:0:5}"

# semmi...

Anélkül, hogy értenék Pythonhoz, a beparsolt objektumból dict lesz, aminek semmi köze a JSON-hoz, csak történetesen az is kapcsoszárójeleket használ amikor stringgé alakítod. Próbáld ki, hogy az egyik property egy boolean vagy null, azoknak is más a string reprezentációja.

Látszik, hogy a Python sokszínű struktúráit nem célszerű egyben odadobni a shell felé. Lásd:
  - list .. többféle változótípussal
  - dict
  - tuple
  - set

Viszont ha Python-ból lebontod alap típusra, és abból automatikusan vagy explicit stringe(ke)t formálsz és azt adod át argumentumok formájában, akkor a shell-nél már semmi probléma.
Továbbá ügyelni kell arra, hogy a shell string delimiter karakterei és szóköz érzékenysége miatt a megfelelő idézőjel (szimpla vagy dupla), netán escape-elve a stringben levő idézőjelet, szóközt, stb. legyen átadva a shellnek.

In [1]: import json

In [2]: a = json.loads('{"a":"b","c":1}')

In [3]: !echo $a
{a: b, c: 1}

In [4]: b = a['c']

In [5]: !echo $b
1

In [6]: q = "1'2"

In [7]: !echo "$q"   # szóközt átviszi, de a stringben levő " karakterre Python-ból kell odafigyelni
1'2

In [8]: q = "1\"2"   # vagy q = '1"2'

In [9]: q
Out[9]: '1"2'

In [10]: !echo "$q"
/bin/bash: -c: sor: 1: váratlan EOF „"” helyett
/bin/bash: -c: sor: 2: szintaktikai hiba: váratlan fájlvége

In [11]: q = q.replace('"', '\\"')

In [12]: q
Out[12]: '1\\"2'

In [13]: !echo "$q"
1"2

igen, épp ez az ami böki a csőrömet, hogy külön figyelni kell a változó átadására.

hogy gördülékenyebben legyen python+bash integrálva én exportálnék minden változót mielőtt elindítom a bash-t és akkor nem kell a 40 féle bash-es quotation syntaxszal kontárkodni; nem kell replace-elni egyaltalán: a "!" utáni részt egy az egyben át lehet adni a bashnak.

persze ennek van hátulütője is, mert ugye így pl nem használhatsz egy PATH nevű python valtozót büntetlenül.

azt mondjuk ki lehet kötni hogy az all-caps változókat nem adja át a shellnek, mondván hogy az ilyen env var-ok nagyban befolyasolhatják a bash működését.

aztán ha még eggyel tovább megyek, a bash lehetne olyan kedves és API-ként kiajánlhatná a tokenizáló logikáját, akkor nem kéne minden shellt hívó programmnak jól-rosszul magának implementálnia.

hány shell injection vulnerability született olyanok miatt hogy a hívó program validálni akarta hogy miket ad át egy parancs paramétereinek, aztán shell-en keresztül hívta és a shell máshogy értelmezte a -c utáni stringet.