Toto zjištění vám může ušetřit celý den programování a množství problémů.
Ve smyčce
while read ... do:
zde zpřístupni pole a ukládej do něj hodnoty
done
nepřežije pole, které jste nadeklarovali jako globální proměnnou pomocí declare -A
protože smyčka while read otevírá nový subshell. Po ukončení smyčky se ukončuje také shell, a to co vidíte na konci této smyčky už není to stejné pole, se kterým pracovala smyčka. Navenek se zdá že má stejný název, tak jde o to samé pole.
process_directories() {
for dir in $(find "$source_path" -type d); do
stats=$(stat -c '%U %G %A' "$dir")
owner=$(echo "$stats" | awk '{print $1}')
group=$(echo "$stats" | awk '{print $2}')
rights=$(echo "$stats" | awk '{print $3}')
owner_representation=$(get_owner_representation "$owner")
group_representation=$(get_group_representation "$group")
rights_short=""
case "$rights" in
"drwxr-xr-x") rights_short="D" ;;
"drwx------") rights_short="d" ;;
"drwxrwxr-x") rights_short="U" ;;
"drwxrwxrwx") rights_short="s" ;;
*) rights_short=$(stat -c '%a' "$dir") ;;
esac
echo "$owner_representation:$group_representation:$rights_short:0:0:$dir" >> "$dir_rights_file"
done
}
"Výše jsou ve smyčce uvedeny dvě funkce, které mají vrátit a přiradit hodnotu. Podívají se nejprve jestli hodnota existuje v poli a když ne, tak ji tam přidají. Problém je, že toto je užitečné jen uvnitř té smyčky. Nelze to ty hodnoty pole externě zapsat do souboru až po ukončení smyčky. A zápis uvnitř smyčky je neefektivní.
Když jsem používal ChatGPT 4o k porovnávání dvou funkcí, původní funkce ve které to funguje, volané uvnitř té smyčky a kopie té funkce pojmenované jako test_fnc ale volaná externě, tak GPT napsal toto:
Rozumím vašemu problému a po dalším prozkoumání jsem zjistil, že rozdíl může být způsoben způsobem, jakým funkce get_owner_representation
a test_fnc
interagují s asociativním polem owners
.
Hlavním rozdílem mezi voláním těchto funkcí uvnitř process_directories
a mimo něj je kontext, ve kterém se spouští. Když používáte příkaz find
s konstrukcí while read
, nový subshell je vytvořen, což může ovlivnit viditelnost změn provedených ve vašem asociativním poli."
Takže jsem došel k jasnému závěru:
Změny, které byly provedeny v poli v rámci toho nově vytvořeného shellu nepřežijí ten shell proto funkce write_config_file nemá co zapisovat: process_directories # shell byl ukončen a změny provedené v poli vlastně byly provedeny do jiného pole, takže toto pole bylo ukončeno a neexistuje. Zatímco v následujícím kontextu existuje to staré pole beze změn. write_config_file
GPT následně navrhl toto nové řešení, ve které se nebude používat subshell:
Máte pravdu, že problém je způsoben tím, že find
s while read
běží v subshellu, takže změny provedené v asociativním poli v rámci tohoto subshellu se nepromítají zpět do rodičovského shellu. Řešením je vyhnout se použití subshellu nebo najít způsob, jak zajistit, aby změny byly zachovány.
Jednou z možností je použít jiný způsob iterace přes soubory a složky, který nevyžaduje použití subshellu. Můžeme nahradit find
s while read
cyklem for
:
#!/bin/bash
# Definování souborů a proměnných
config_file="config.txt"
dir_rights_file="dir_rights.txt"
file_rights_file="file_rights.txt"
source_path="$1" # Získání cesty ze vstupního argumentu
# Písmena pro vlastníky a skupiny
owner_letters=(a b c d e f g h i j k l m n o p q r s t u v w x y z)
group_letters=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
# Inicializace polí
declare -A owners
declare -A groups
# Funkce pro inicializaci polí
initialize_arrays() {
for letter in "${owner_letters[@]}"; do
owners[$letter]=""
done
for letter in "${group_letters[@]}"; do
groups[$letter]=""
done
}
# Funkce pro přiřazení písmena pro vlastníky
get_owner_representation() {
local owner=$1
local letter=${owner:0:1} # Získejte první písmeno vlastníka
local representation=$letter
echo "DEBUG: owner=$owner, letter=$letter, owners[$letter]=${owners[$letter]}"
if [ -z "${owners[$letter]}" ]; then
owners[$letter]=$owner
echo "DEBUG: owners[$letter] was empty, now set to $owner"
elif [ "${owners[$letter]}" != "$owner" ]; then
representation=$owner # Pokud je již písmeno obsazeno jiným vlastníkem, použijte celý název
echo "DEBUG: owners[$letter] was not empty and not equal to $owner, using full name"
fi
echo "$representation"
}
test_fnc() {
local owner=$1
local letter=${owner:0:1} # Získejte první písmeno vlastníka
local representation=$letter
echo "DEBUG: test_fnc - owner=$owner, letter=$letter, owners[$letter]=${owners[$letter]}"
if [ -z "${owners[$letter]}" ]; then
owners[$letter]=$owner
echo "DEBUG: test_fnc - owners[$letter] was empty, now set to $owner"
elif [ "${owners[$letter]}" != "$owner" ]; then
representation=$owner # Pokud je již písmeno obsazeno jiným vlastníkem, použijte celý název
echo "DEBUG: test_fnc - owners[$letter] was not empty and not equal to $owner, using full name"
fi
echo "$representation"
}
# Funkce pro zápis do konfiguračního souboru
write_config_file() {
> "$config_file" # Vymazat existující soubor
for letter in "${owner_letters[@]}"; do
if [ -n "${owners[$letter]}" ]; then
echo "$letter:${owners[$letter]}" >> "$config_file"
else
echo "$letter:" >> "$config_file"
fi
done
for letter in "${group_letters[@]}"; do
if [ -n "${groups[$letter]}" ]; then
echo "$letter:${groups[$letter]}" >> "$config_file"
else
echo "$letter:" >> "$config_file"
fi
done
}
# Funkce pro zpracování složek a zápis práv
process_directories() {
for dir in $(find "$source_path" -type d); do
stats=$(stat -c '%U %G %A' "$dir")
owner=$(echo "$stats" | awk '{print $1}')
group=$(echo "$stats" | awk '{print $2}')
rights=$(echo "$stats" | awk '{print $3}')
owner_representation=$(get_owner_representation "$owner")
group_representation=$(get_group_representation "$group")
rights_short=""
case "$rights" in
"drwxr-xr-x") rights_short="D" ;;
"drwx------") rights_short="d" ;;
"drwxrwxr-x") rights_short="U" ;;
"drwxrwxrwx") rights_short="s" ;;
*) rights_short=$(stat -c '%a' "$dir") ;;
esac
echo "$owner_representation:$group_representation:$rights_short:0:0:$dir" >> "$dir_rights_file"
done
}
print_associative_array() {
local -n arr=$1
for key in "${!arr[@]}"; do
echo "$key: ${arr[$key]}"
done
}
clear
# Hlavní část skriptu
initialize_arrays
echo "After initialize_arrays: print owners"
print_associative_array owners
# Zavolejte get_owner_representation před voláním process_directories, abyste nastavili hodnotu pro "root"
get_owner_representation "root"
process_directories
echo "process_directories: call test_fnc"
test_fnc "david"
echo "After test_fnc: print owners"
print_associative_array owners
write_config_file
echo "Configuration file has been created: $config_file"
echo "Directory rights file has been created: $dir_rights_file"
PŮVODNÍ KÓD KTERÝ NEFUNGOVAL SPRÁVNĚ
#!/bin/bash
# Definování souborů a proměnných
config_file="config.txt"
dir_rights_file="dir_rights.txt"
file_rights_file="file_rights.txt"
source_path="$1" # Získání cesty ze vstupního argumentu
# Písmena pro vlastníky a skupiny
owner_letters=(a b c d e f g h i j k l m n o p q r s t u v w x y z)
group_letters=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
# Inicializace polí
declare -A owners
declare -A groups
# Funkce pro inicializaci polí
initialize_arrays() {
for letter in "${owner_letters[@]}"; do
owners[$letter]=""
done
for letter in "${group_letters[@]}"; do
groups[$letter]=""
done
}
test_fnc() {
local owner=$1
local letter=${owner:0:1} # Získejte první písmeno vlastníka
local representation=$letter
if [ -z "${owners[$letter]}" ]; then
owners[$letter]=$owner
echo "A:${owners[$letter]} it was set '$owner'"
elif [ "${owners[$letter]}" != "$owner" ]; then
representation=$owner # Pokud je již písmeno obsazeno jiným vlastníkem, použijte celý název
fi
echo "$representation"
}
# Funkce pro přiřazení písmena pro vlastníky
get_owner_representation() {
local owner=$1
local letter=${owner:0:1} # Získejte první písmeno vlastníka
local representation=$letter
echo "DEBUG: owner=$owner, letter=$letter, owners[$letter]=${owners[$letter]}"
if [ -z "${owners[$letter]}" ]; then
owners[$letter]=$owner
echo "DEBUG: owners[$letter] was empty, now set to $owner"
elif [ "${owners[$letter]}" != "$owner" ]; then
representation=$owner # Pokud je již písmeno obsazeno jiným vlastníkem, použijte celý název
echo "DEBUG: owners[$letter] was not empty and not equal to $owner, using full name"
fi
echo "$representation"
}
# Funkce pro přiřazení písmena pro skupiny
get_group_representation() {
local group=$1
local letter=$(echo "${group:0:1}" | tr '[:lower:]' '[:upper:]') # Získejte první písmeno skupiny a převedení na velké písmeno
local representation=$letter
if [ -z "${groups[$letter]}" ]; then
groups[$letter]=$group
elif [ "${groups[$letter]}" != "$group" ]; then
representation=$group # Pokud je již písmeno obsazeno jinou skupinou, použijte celý název
fi
echo "$representation"
}
# Funkce pro zápis do konfiguračního souboru
write_config_file() {
> "$config_file" # Vymazat existující soubor
for letter in "${owner_letters[@]}"; do
if [ -n "${owners[$letter]}" ]; then
echo "$letter:${owners[$letter]}" >> "$config_file"
else
echo "$letter:" >> "$config_file"
fi
done
for letter in "${group_letters[@]}"; do
if [ -n "${groups[$letter]}" ]; then
echo "$letter:${groups[$letter]}" >> "$config_file"
else
echo "$letter:" >> "$config_file"
fi
done
}
# Funkce pro zpracování složek a zápis práv
process_directories() {
find "$source_path" -type d | while IFS= read -r dir; do
# owner=$(stat -c '%U' "$dir")
# group=$(stat -c '%G' "$dir")
# rights=$(stat -c '%A' "$dir")
stats=$(stat -c '%U %G %A' "$dir")
owner=$(echo "$stats" | awk '{print $1}')
group=$(echo "$stats" | awk '{print $2}')
rights=$(echo "$stats" | awk '{print $3}')
# echo "Before test_fnc: ${owners[@]}"
# test_fnc "$owner"
# echo "After test_fnc: ${owners[@]}"
# sleep 20
# Přiřaďte písmeno pro vlastníka
get_owner_representation "root"
owner_representation=$(get_owner_representation "$owner")
# Přiřaďte písmeno pro skupinu
group_representation=$(get_group_representation "$group")
# Zjištění práv
rights_short=""
case "$rights" in
"drwxr-xr-x") rights_short="D" ;;
"drwx------") rights_short="d" ;;
"drwxrwxr-x") rights_short="U" ;;
"drwxrwxrwx") rights_short="s" ;;
*) rights_short=$(stat -c '%a' "$dir") ;;
esac
echo "$owner_representation:$group_representation:$rights_short:0:0:$dir" >> "$dir_rights_file"
done
}
print_associative_array() {
local -n arr=$1
for key in "${!arr[@]}"; do
echo "$key: ${arr[$key]}"
done
}
# Hlavní část skriptu
initialize_arrays
echo "After initialize_arrays: print owners"
print_associative_array owners
process_directories
echo "process_directories: call test_fnc"
test_fnc "david"
echo "After test_fnc: print owners"
print_associative_array owners
write_config_file
echo "Configuration file has been created: $config_file"
echo "Directory rights file has been created: $dir_rights_file"
Žádné komentáře:
Okomentovat