Portál AbcLinuxu, 5. května 2025 23:29
if [ a + [ b - 1 ] < c ]; then if [[ a + [[ b - 1 ]] < c ]]; then if (( a + (( b - 1 )) < c )); then if (( a + $( b - 1 ) < c )); then
Řešení dotazu:
..je na místě zvolit jiný jazyk.Díky, jiný jazyk? On existuje i jiný jazyk pro práci s linuxem?
apt install python3.5 Reading package lists... Done Building dependency tree Reading state information... Done python3.5 is already the newest version (3.5.3-1). 0 upgraded, 0 newly installed, 0 to remove and 3 not upgraded. python -V Python 2.7.13Díval jsem se do /etc a jsou tam složky
python python2.7 python3 python3.5ale všechny jsou prázdné (v každé je jeden soubor). Kde ty soubory jsou? Díky
python3.5 is already the newest version (3.5.3-1).
Již jej máš instalovaný, python je na většině běžných distribucí v základu.
V /etc jsou system-wide konfigurace, vlastní software je rozmístěný v odpovídajících adresářích.
Opravdu je potřeba si nejdříve o linuxu něco trošku přečíst.
python3 -V
Jejda Python trošičku znám, ale myslel jsem, že je pro Windows.
Podivná pověra. Žádný kloudný programovací jazyk není "pro Windows" a nevznikl na Windows. (Příznivci C# teď nabíjejí kulomety, ale co už.) Moderní programování jako takové vzniklo na systémech UNIXového typu (s jazyky jako C, C++ nebo Java). Windows jsou prostředí nepřátelské k programování i ke studiu informačních technologií obecně.
ale všechny jsou prázdné (v každé je jeden soubor). Kde ty soubory jsou? Díky
Bohužel neuvádíš, o jakou distribuci se jedná. Předpokládám, že o nějakou založenou na dpkg
a spol. Takže:
which python whereis python dpkg -l | awk '$2 ~ /python/ {print}' dpkg -L python dpkg -L python-minimal
Detaily, co ty příkazy vypisují, jsou v manuálových stránkách.
Právě to rozepsání do více příkazů způsobí zoufalou nečitelnost skriptu.
Rozhodně je na místě přečíst si pořádně manuálovou stránku Bashe. Je sice dlouhá a spletitá, ale v drtivé většině skriptů, které vidím, se setkávám s naprostým nepochopením, jak fungují v Bashi datové typy, substituce, pole, asociativní pole, aritmetika a cykly. Někteří autoři skriptů na čtení manuálové stránky rezignovali a kvůli zdánlivě netriviálním trivialitám spouštějí (klidně v cyklech) procesy jako awk
nebo sed
. Pak jsou skripty pomalé. Manipulace s proměnnými v čistém Bashi, při které se nespouští externí procesy, většinou na zpracování textových dat do stovek MB velikosti zcela postačuje, pokud jde o "efektivitu".
Silnou stránkou Bashe je například jednoduchý multiprocesing, který se dá ve spoustě případů použít pro paralelní zpracování dat, třeba nad výstupem z příkazu find
. Přesně tam se podmínky a cykly hodí víc než dobře:
set -e declare -ri TASKS_TO_RUN=128 declare -ri TASKS_LIMIT=16 declare -i tasks=0 run() { local -r task_name="$1"; shift if ((tasks > TASKS_LIMIT)); then wait -n; else ((++tasks)); fi ( "$@"; echo "${task_name} done."; )& } wait_for_completion() { while ((tasks--)); do wait -n; done; } for ((i = 0; i < TASKS_TO_RUN; ++i)); do run "Task ${i}" sleep ".$((100 * RANDOM / 32768))" done wait_for_completion
wait_for_completion
, když by měl stačit vestavěný příkaz wait
?
Používání globálních proměnných je fakt hnus.
Jsem jedno ucho, jak se v Bashi v tomhle konkrétním případě zbavíš globálních proměnných. Tak prosím, příklady jsou vítané. A pokud možno aby nebyly hnus.
Mimochodem, globální konstanty rozhodně nejsou hnus a globální počítadla taky ne — to jenom připomínám pro úplnost.
Asi si umíš představit, že wait_for_completion
by mohl mít (v původní, složitější verzi takového skriptu) za úkol třeba vypisovat údaje o počtu zbývajících procesů, že ano.
find
, zbavíš se konstanty TASKS_TO_RUN
a budeš to mít v podobě, kterou používám:
#!/bin/bash set -e declare -ri TASKS_TO_RUN=12 declare -ri TASKS_LIMIT=4 for ((i = 0; i < TASKS_TO_RUN; ++i)); do echo -ne "./subproces.sh Task_${i} sleep .$((100 * RANDOM / 32768)) \00" done | xargs -0 -n 1 -P $TASKS_LIMIT bash -cSoubor
subproces.sh
zpravidla není skript, ale konkrétní program, který data ze souboru zpracuje. Tohle je jen pro ilustraci:
#!/bin/bash set -e declare -r task_name="$1" shift "$@" echo "${task_name} done."Jak vidíš, žádnou globální proměnnou jsem nepoužil. Data předávám pouze přes roury a parametry.
To nakonec skvěle ilustruje, do jaké míry je ten „hnus“ spíš věcí názoru. Řešení s
xargs
je svým způsobem pěkné, ale nehodí se třeba pro (zmíněné) hlášení počtu zbývajících procesů nebo pro (méně triviální) přesměrování standardního vstupu i výstupu z těch procesů, což můžou být pojmenované roury (mkfifo
), soubory atd. Samozřejmě to taky jde — s použitím toho odděleného skriptu, který to zařídí —, ale pak se nabízí znova otázka, co je „hnus“ a co až tolik ne.
Problém s globální proměnnou je vlastně spíš slovíčkaření, protože přesměrování celého výstupu do xargs
je v mnoha směrech horší než globální proměnná. Přístup s run
a wait_for_completion
umožňuje skriptu normálně používat stdout
i stderr
, paralelně dělat i jiné úkony než generování argumentů pro xargs
(ptát se uživatele v terminálu, jestli chce spustit i tyhle další procesy nebo třeba ještě ne), tu a tam spustit něco na pozadí pomocí run
a pak na to třeba počkat, když je nutná synchronizace mezi nějakými fázemi toho výpočtu.
Řešení s xargs
bude vždycky tomu shellu blokovat některý ze standardních výstupů. Může sice číst z pojmenované roury, ale do té roury se pak musí přesměrovat celý blok (for
, while
apod.), protože přesměrovat tam třeba něco jen tak z echo
znamená, že se ta roura hned zavře a xargs
už ji dál číst nebude. To není moc flexibilní. Můžu jistě celý vstup pro xargs
vygenerovat předem do pole řádků nebo jiné struktury, ale to jsme zase u toho hnusu.
Mimochodem, právě různá řešení s xargs
jsem ve spoustě skriptů nahradil jednoduchými subshelly, protože xargs
není dost flexibilní pro účely, ke kterým ty skripty byly.
Další killer feature ampersandu je, že po každém &
se dá přečíst $!
. Něco takového xargs
taky neumí. Tedy lze mít například pole s PID všech spuštěných procesů na pozadí a lze čekat (pomocí wait
) na každý z nich zvlášť a získat a zpracovat jeho návratovou hodnotu.
Pro ilustraci těch výsledků procesů na pozadí:
background_pids=() exit_codes=() # ... run_something & background_pids[${#background_pids[@]}]="$!" run_something_else & background_pids[${#background_pids[@]}]="$!" # ... for idx in "${!background_pids[@]}"; do wait "${background_pids[idx]}" exit_codes[idx]="$?" done # ...
Tohle^^^ xargs
sice možná dá taky, ale způsobem příliš obskurním.
if (( a + ( b - 1 ) < c )); thennebo takto
if (( a + $(( b - 1 )) < c )); thenale když chci výsledek do proměnné tak takto?
VAR=$( a + b )
VAR=$(( a + b ))
Tohle přece můžeš snadno zkusit. První, druhý a čtvrtý příklad hodí syntaktickou chybu, takže to asi nebude ono.
Třetí příklad je v podstatě OK, jen není důvod mít tam dvojité závorky, když už jsi v aritmetickém výrazu.
a=3 b=4 c=6 if (( a + (b - 1) < c )); then echo jo; else echo ne; fi
Bash umí taky (trochu) specifikovat datové typy (integer, indexované pole, asociativní pole) a typ integer umí jednodušší syntaxi přiřazení (bez $(())
), jako například proměnná e2
níže.
declare -i e2 e1='a + (b - 1)' e2='a + (b - 1)' e3=$((a + (b - 1))) echo "$e1" # a + (b - 1) echo "$e2" # 6 echo "$e3" # 6
Podmínky a výrazy se samozřejmě dají použít taky ve for-cyklu.
c=20 for ((a = 1, b = 10; a + (b - 1) < c; a += 2, ++b)); do echo "a: ${a} b: ${b} c: ${c} a + (b - 1): $((a + (b - 1)))" done
Nebo v jiných cyklech.
a=1 b=10 c=20 while ((a + (b - 1) < c)); do echo "a: ${a} b: ${b} c: ${c} a + (b - 1): $((a + (b - 1)))" ((a += 2)) ((++b)) done
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.