Jag försöker skapa användarkataloger baserat på en importerad passwd-fil och försöker ladda in data i en associativ matris: array [username] = katalog. Jag kan ladda fälten i separata matriser men jag kan inte få associationerna korrekta, eftersom varje fält blir associerat med alla kataloger.
USERLIST=$(cat /usrmkr/in.out | awk -F ":" "{print $1}") DIRLIST=$(cat /usrmkr/in.out | awk -F ":" "{print $6}") declare -A USERARRAY func_StoreData() { USERARRAY[$1]="$2" return $? } for ((u=0;u<${USERLIST[@]};u++)); do func_StoreData ${USERLIST[$u]} ${DIRLIST[$u]} done for i in ${!USERARRAY[@]}; do echo "making directory for $i in ${USERARRAY[$i]}" #Do stuff done
Kommentarer
- Om något av de befintliga svaren löser ditt problem, överväg att acceptera det via bocken. Tack!
Svar
Du kan läsa hela saken med bash direkt och berätta att läsa att dela på kolon :
declare -A userarray while IFS=: read -r username password uid gid gecos home shell; do userarray[$username]=$home done < /usrmkr/in.out
Svar
eval declare -A USERARRAY=( $(awk -F: "{ printf "[\"%s\"]=\"%s\"\n", $1, $6}" /usrmkr/in.out) )
awk
-skriptet ger output i [key]=val
-formatet krävs vid inställning av flera element i en bash-associerande matris och dubbla citat både nyckeln och värdet (["key"]="value"
) om det finns mellanslag, flikar etc i antingen tangenterna eller värdena .
Jag har använt \n
som separator för att göra det enkelt om du vill efterbearbeta awk
utdata med något annat verktyg (även om awk
kan göra det mesta du kanske vill göra med andra verktyg, ändå).
Kommandosubstitution borde vara tillräckligt av sig själv .. .men på grund av vad som är, IMO, ett fel i bash
, om det första icke-blankstegstecknet i ( ... )
-definitionen inte är ta [
, det ger bara felmeddelandet must use subscript when assigning associative array
.
t.ex. båda följande försök att ställa in USERARRAY misslyckas:
$ bash --version | head -1 GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu) $ declare -A USERARRAY=($(awk -F: "{ printf "[\"%s\"]=\"%s\"\n", $1, $6}" /usrmkr/in.out)) bash: USERARRAY: $(awk -F: "{ printf "[\"%s\"]=\"%s\"\n", $1, $6}" /usrmkr/in.out): must use subscript when assigning associative array $ UA=$(awk -F: "{ printf "[\"%s\"]=\"%s\"\n", $1, $6}" /usrmkr/in.out) $ declare -A USERARRAY=( $UA ) bash: USERARRAY: $UA: must use subscript when assigning associative array
Lösningen är att använda eval
när deklarerar den hashade arrayen, som i kodexemplet högst upp i mitt svar. Eller
eval declare -A USERARRY=( $UA )
Kommentarer
- 1. detta använder
eval
– naturligtvis finns det ' risk för kommandoinjektion. Jag tror inte ' att det ' är ett problem i det här fallet, eftersom mitt svar är avsett för användning med OP ' s specifika & antagligen kontrollerade / kända bra inmatningsfiler. 2. " glömde att citera utvidgningarna " – va? Vad? Jag ser ' inget citatproblem – awk-koden citerar uttryckligen både nycklar och vals för arrayen. 3. BTW, nästan 2 år senare, låter bash v4.4.19 (1) -release still inte ' du inte kan använda en cmd-subst direkt eller till och med a var i en hashdeklaration:declare -A array=( $(...) )
misslyckas med samma fel, så eval krävs fortfarande - @St é phaneChazelas Om det ' är ett citeringsproblem, förklara hur och varför – helst med ett verkligt exempel med giltig / etc / passwd-stilinmatning, vilket är relevant för denna fråga och svar, inte bara en generisk och uppenbar " om du matar in skräpdata får du ' oförutsägbara och potentiellt farliga resultat ".
- Du ' har fått en poäng att det vanligtvis inte skulle vara ett problem för typiska
/etc/passwd
data. Så låt mig omformulera det: ⚠ även om det borde vara OK för typiska/etc/passwd
-data, bör det tillvägagångssättet inte användas för godtyckliga data eftersom det skulle utgöra en sårbarhet vid kommandot. Specifikt antar den att data inte ' t innehåller,`
,"
, backslash,$
,*
,?
,[
, mellanslag eller NUL-tecken. - Om citatproblemet, se till exempel Säkerhetsimplikationer av att glömma att citera en variabel i bash / POSIX-skal och frågorna som länkas där för att se vad det innebär att lämna utvidgningarna ociterade.
- Observera att äldre versioner av bash hade ett fel där ' a = (" $ x ") `misslyckades med att skapa en matris med ett element som innehållet i
$x
när$x
var något som[123]=qwe
(det var före bash-stödda associerade matriser).Det ' s sanna bash kunde ha valt att tillåta associerande matriser att deklareras soma=("$x")
(där$x
måste börja med[...]=
) utövera=([$k]=$v)
syntax men då skulle det ha behövt definiera ett sätt att specificera tangenter som innehåller] tecken. I vilket fall som helst skulle jag inte kalla det en bugg utan ett designbeslut (här gjord av ksh där bash kopierade sin syntax från).
Svar
I stället för att slå samman två listor kan vi bygga arrayen i en enda slinga (och ett enda samtal till awk
för gott mått) genom att returnera en lista av användare: dir-poster och sedan dela upp det med variabla utvidgningar:
#!/bin/bash declare -A USERARRAY for u in $(awk -F: "{print $1 ":" $6}" /usrmkr/in.out) do user=${u%:*} dir=${u#*:} USERARRAY[$user]=$dir done
Svar
zsh
har en mer användbar, vanligare (liknar tcl
eller perl
) sätt att förklara associerande matriser som en helhet: array=(key1 value1 key2 value2...)
typeset -A userarray IFS=$":\n\n" userarray=($(cut -d : -f 1,6 < /usrmkr/in.out))
Fördubbling \n
, som i ksh93
tar bort newline ”s specialstatus som ett IFS-mellanslagstecken . Utan det foo:\nbar:x
skulle delas upp i ”foo”, ”bar”, ”x” istället för ”foo”, ””, ”bar”, ”x”.
Med , syntaxen är samma besvärliga som i ksh93
: array=([key1]=value1 [key2]=value2)
, så du kan inte enkelt få utdata av ett kommando till en associerande array annan än genom att använda en loop som gör en enskild elementtilldelning åt gången som andra har visat.
Observera att bash
associerande matriser ( i motsats till ksh93
eller zsh
) har en begränsning genom att nyckeln inte kan vara den tomma strängen (inte ett problem här). En annan begränsning, den här gången delad med ksh93
är att varken nyckel eller värden kan innehålla NUL-byten (inte heller ett problem här).
(stöd för associerande arrays var i ksh93 från början (1993), läggs till zsh
1998 (3.1.5-pws-3) och bash
2009 ( 4.0))
Kommentarer
- Varför satte du ' \ n ' två gånger till IFS?
- @rubystallion se redigera