2016-01-12 24 views
5

mój plik python ma te 2 zmienne:Czy czytać zmienną Pythona w skrypcie powłoki?

week_date = "01/03/16-01/09/16" 
cust_id = "12345" 

jak można przeczytać w skrypcie powłoki, które odbywają się w tych 2 zmiennych?

Mój obecny skrypt powłoki wymaga ręcznej edycji "dt" i "id". Chcę odczytać zmienne Pythona w skrypcie powłoki, więc mogę po prostu edytować mój plik parametrów Pythona, a nie tak wiele plików.

plik shell:

#!/bin/sh 

dt="01/03/16-01/09/16" 
cust_id="12345" 

w nowym pliku Pythona mogłyby po prostu zaimportować plik parametr Pythona.

+0

Są ustawione gdzie? Na poziomie modułu? –

+0

jest na poziomie modułu – jxn

+1

BTW, używając '#!/Bin/sh' shebang lub wywołując go za pomocą' sh scriptname' sprawia, że ​​twój skrypt jest skryptem POSIX sh, a nie skryptem bash. Ponieważ twoje pytanie jest oznaczone jako bash, zakładam, że to był błąd i że chciałeś użyć bash. –

Odpowiedz

8

Zastanów się coś podobnego do następującego:

#!/bin/bash 
#  ^^^^ NOT /bin/sh, which doesn't have process substitution available. 

python_script=' 
import sys 
d = {}         # create a context for variables 
exec(open(sys.argv[1], "r").read()) in d # execute the Python code in that context 
for k in sys.argv[2:]: 
    print "%s\0" % str(d[k]).split("\0")[0] # ...and extract your strings NUL-delimited 
' 

read_python_vars() { 
    local python_file=$1; shift 
    local varname 
    for varname; do 
    IFS= read -r -d '' "${varname#*:}" 
    done < <(python -c "$python_script" "$python_file" "${@%%:*}") 
} 

Można wówczas wykorzystać jako:

read_python_vars config.py week_date:dt cust_id:id 
echo "Customer id is $id; date range is $dt" 

... lub, jeśli nie chcesz zmieniać nazwy zmiennych, gdyż były one czytać, po prostu:

read_python_vars config.py week_date cust_id 
echo "Customer id is $cust_id; date range is $week_date" 

Zalety:

  • przeciwieństwie naiwnej rozwiązania oparte regex (które mają problemy z niektórymi szczegółami Python parsowania - spróbuj nauczania sed obsługiwać zarówno surowych i regularne ciągi, a zarówno pojedynczych i potrójnych cudzysłowów bez dokonywania to do włosa!) lub podobnego podejścia, które wykorzystywało wyjście rozdzielone znakiem nowej linii z podprocesu Python, to poprawnie obsłuży każdy obiekt, dla którego str() daje reprezentację bez znaków NUL, których może użyć skrypt powłoki.
  • Uruchamianie zawartości za pomocą interpretera w języku Python oznacza również możliwość programowego określania wartości - na przykład, możesz mieć kod w języku Python, który prosi system kontroli wersji o datę ostatniej zmiany odpowiedniej treści.

    Pomyśl o sytuacjach, takich jak ten:

    start_date = '01/03/16' 
    end_date = '01/09/16' 
    week_date = '%s-%s' % (start_date, end_date) 
    

    ... przy użyciu interpretera Pythona do analizowania Python oznacza, że ​​nie są ograniczające, jak ludzie mogą aktualizować/zmodyfikować plik konfiguracyjny Pythona w przyszłości.

Teraz pomówmy Ostrzeżenia:

  • Jeśli kod Python ma skutki uboczne, takie skutki uboczne będą oczywiście obowiązywać (podobnie jak to, gdyby została wybrana do import plik jako moduł w Pyton). Nie używaj tego do wyodrębnienia konfiguracji z pliku, którego zawartości nie masz zaufania.
  • Łańcuchy w języku Python są w stylu Pascala: Mogą zawierać literalne wartości NUL. Łańcuchy w językach powłoki są w stylu C: Są one kończone przez pierwszy znak NUL. Tak więc niektóre zmienne mogą istnieć w Pythonie, niż nie mogą być reprezentowane w powłoce bez nieliteralnego ucieczki. Aby zapobiec obiektowi, którego reprezentacja str() zawiera NUL-y, przechodząc do przodu w inne przypisania, ten kod kończy łańcuchy na ich pierwszej wartości NUL.

Teraz pomówmy o szczegóły wykonania.

  • ${@%%:*} jest rozszerzenie [email protected] który przycina wszystkie zawartości i po tym pierwszym : w każdym z argumentów, co przechodząc tylko nazwy zmiennych Pythonowi interpretera. Podobnie, ${varname#*:} jest rozwinięciem, które wyrównuje wszystko do pierwszej wartości : włącznie i przekazuje ją do read. Zobacz the bash-hackers page on parameter expansion.
  • Używanie <(python ...) to składnia podstawienia procesu: Wyrażenie <(...) powoduje przekształcenie nazwy pliku, który podczas odczytu zapewni wyjście tego polecenia. Użycie < <(...) przekierowuje dane wyjściowe z tego pliku, a więc polecenie (pierwsze < jest przekierowaniem, podczas gdy drugie jest częścią tokena <(, który rozpoczyna podstawienie procesu). Użycie tego formularza do uzyskania wyniku w pętli while read pozwala uniknąć błędu wymienionego w BashFAQ #24 ("I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?").
  • IFS= read -r -d '' konstrukcja ma szereg elementów, z których każdy sprawia, że ​​zachowanie read bardziej prawdziwe pierwotnej treści:

    • Usuwanie IFS na czas trwania polecenia zapobiega odstępy od przycięcia od końca zawartości zmiennej.
    • Użycie zapobiega odbieraniu dosłownych odwróconych ukośników przez samą siebie, zamiast reprezentowanych na wyjściu.
    • Użycie ustawia pierwszy znak pustego ciągu '' jako ogranicznik rekordów. Ponieważ łańcuchy C są zakończone znakiem NUL, a powłoka używa łańcuchów C, znak ten jest NUL. Zapewnia to, że zawartość zmiennych może zawierać dowolną wartość inną niż NUL, w tym literalne znaki nowej linii.

    Więcej informacji na temat procesu odczytywania zorientowanych na dane rekordów z łańcucha znaków w bash.

+0

jeśli utrzymuję nazwę zmiennej "dt" w skrypcie powłoki, czy będzie to po prostu 'dt = $ week_date' po powyższym kodzie? – jxn

+0

powinno być spacja po drugim '<', aby być <<(python -c "$ python_script" nazwa_pliku filename.py identyfikator_nocny_data) ' – jxn

+0

@jxn, patrz: edycje; Zmieniłem zmienną nazwę w procesie jawnie i próbowałem odpowiedzieć na twoje pytania dotyczące składni '<()'. –

0

Chciałbym zrobić coś takiego. Możesz zmodyfikować to trochę za drobne zmiany do włączenia/wyłączenia cytaty jak ja naprawdę nie testowałem go za scenariusz:

#!/bin/sh 
exec <$python_filename 
while read line 
do 
     match=`echo $line|grep "week_date ="` 
     if [ $? -eq 0 ]; then 
       dt=`echo $line|cut -d '"' -f 2` 
     fi 

     match=`echo $line|grep "cust_id ="` 
     if [ $? -eq 0 ]; then 
       cust_id=`echo $line|cut -d '"' -f 2` 
     fi 
done 
+0

Potrzebuje więcej ofert. Zobacz, co 'echo $ line' robi, jeśli' line = 'hello * world''. –

+1

... i, oczywiście, masz wszystkie zwykłe problemy związane z naiwnym podejściem do analizy łańcuchów. A co jeśli to 'week_date = 'foo'' zamiast' week_date = "foo" '? Lub 'week_date = r'foo''? Lub 'week_date = get_current_datespan()'? –

+1

Ponadto, nie ma sensu sprawdzać osobno '$?'; po prostu umieść 'grep' w swoim' if' oświadczeniu: 'if printf '% s \ n'" $ line "| grep -q 'week_date ='; następnie ' –

2

Inne odpowiedzi dać sposób to zrobić dokładnie to, o co prosisz, ale myśl, że pomysł jest trochę szalony. Istnieje prostszy sposób na spełnienie obu skryptów - przenieś te zmienne do pliku konfiguracyjnego. Możesz nawet zachować prosty format przydziału.

Tworzenie config sam (INI stylu)

dt="01/03/16-01/09/16" 
cust_id="12345" 

W python:

config_vars = {} 
with open('the/file/path', 'r') as f: 
    for line in f: 
     if '=' in line: 
      k,v = line.split('=', 1) 
      config_vars[k] = v 
week_date = config_vars['dt'] 
cust_id = config_vars['cust_id'] 

W bash:

source "the/file/path" 

I nie trzeba robić szalone parsowanie źródła.Alternatywnie możesz po prostu użyć json dla pliku konfiguracyjnego, a następnie użyć modułu json w python i jq w powłoce do parsowania.

+0

Jedyna rzecz, którą mogę zmienić w tej kwestii - jeśli chcesz, aby kod Pythona analizował coś zapisanego w powłoce, rozważ użycie 'shlex', aby faktycznie uzyskać kompatybilność z powłoką (dobrze, kompatybilny z POSIX-sh; obsługiwać rozszerzenia bash takie jak '$ ''' lexing; w ten sposób otrzymasz wsparcie dla przypisań wieloliniowych i tym podobne. –

+0

Tak więc myślałem, że config to ini-style, a nie bash (inaczej wracamy do zwariowanego problemu "parsuj inny język"). Jeśli potrzebujesz ucieczki, multilinii lub czegokolwiek innego, po prostu przejdź z jsonem zamiast ... – viraptor

+0

Coś jak: 'import shlex; config_vars = dict ([item.split ('=', 1) dla pozycji w shlex.split (open ('the/file/path'). read()) if '=' w pozycji]) '; w ten sposób otrzymujesz ucieczkę, multilinię i c. wsparcie i nie musisz sam pisać parsera. :) –

Powiązane problemy