Bash - 다차원 배열 및 출력에서 ​​변수 추출

Bash - 다차원 배열 및 출력에서 ​​변수 추출

나는 간단한 일을 하고 싶지만 여기서 내 목표를 달성하는 방법을 잘 모르겠습니다.

w콘솔에서 명령어로 주어진 USER, TTY, FROM 값을 추출하려고 합니다. Bash에서는 이 출력을 가져와 값을 다차원 배열(또는 공백 구분 기호가 있는 배열)에 넣으려고 합니다.

#!/bin/bash
w|awk '{if(NR > 2) print $1,$2,$3}' | while read line
do
     USERS+=("$line")
     echo ${#USERS[@]}
done
echo ${#USERS[@]}

단일 배열에서 값을 행 단위로 읽는 방법을 찾았지만 USERS 배열 값을 while 루프의 범위 밖으로 이동할 수는 없는 것 같습니다. 루프 후에 1,2,3,4 값을 인쇄한 다음 0을 인쇄합니다. 내가 읽은 모든 예제는 범위 밖의 변수를 잘 활용하지만, 그럴 수는 없는 것 같습니다.

답변1

주요 문제는 파이프라인의 마지막 명령이 파이프라인의 다른 모든 명령과 마찬가지로 하위 셸에서 실행된다는 것입니다. 이것은 대부분의 껍질의 경우입니다. ATT ksh 및 zsh는 예외입니다. 이들은 상위 셸에서 파이프라인의 마지막 명령을 실행합니다.

Bash 4.2부터 설정을 통해 bash가 ksh 및 zsh처럼 작동하도록 지시할 수 있습니다.lastpipe 옵션.

#!/bin/bash
USERS=()
shopt -s lastpipe
w | awk '{if(NR > 2) print $1,$2,$3}' | while read line; do
  USERS+=("$line")
done
echo ${#USERS[@]}

또는 다음을 사용할 수 있습니다.프로세스 교체파이프 대신 read명령이 기본 셸 프로세스에서 실행되도록 합니다.

#!/bin/bash
USERS=()
while read line; do
  USERS+=("$line")
done < <(w | awk '{if(NR > 2) print $1,$2,$3}')
echo ${#USERS[@]}

또는 Bourne, dash 및 pdksh와 같이 프로세스 대체나 ksh/zsh 동작 없이 셸에서 작동하는 이식 가능한 접근 방식을 사용할 수 있습니다. (배열의 경우 여전히 (pd)ksh, bash 또는 zsh가 필요합니다.) 파이프의 데이터가 필요한 모든 것은 파이프 내부에서 실행됩니다.

#!/bin/bash
USERS=()
shopt -s lastpipe
w | awk '{if(NR > 2) print $1,$2,$3}' | {
  while read line; do
    USERS+=("$line")
  done
  echo ${#USERS[@]}
}

답변2

shopt -s lastpipe파이프라인의 마지막 명령을 사용하여 현재 셸 환경으로 들어갈 수 있습니다 . 그러면 문제가 해결됩니다. 내 생각에 이 기능은 bash에 항상 존재하는 것은 아니므로 광범위하게 호환되는 코드를 원한다면 이 기능을 사용하지 마십시오.

호환 가능한 대안:

export_array="$(w | awk '{if(NR > 2) print $1,$2,$3}' | 
  { USERS=(); while read line; do
      USERS[]="$line"
    done
    declare -p USERS; } )"
eval "$export_array"

답변3

Bash 배열은 1차원 배열입니다. 각 행에 대해 순서가 지정된 별도의 값을 저장하려면 연관 배열을 사용하는 것이 한 가지 해결책입니다. 대략적인 예:

또한 대문자 변수 이름은 환경 변수와 충돌할 수 있으므로 주의하세요.

#!/bin/bash

declare -i i=0 j=0
declare -A w

while read -r user tty from _;do
    ((++i > 2)) || continue
    w["$j.user"]="$user"
    w["$j.tty"]="$tty"
    w["$j.from"]="$from"
    ((++j))
done < <(w)

for ((i = 0; i < j; ++i)); do
    printf "entry %-2d {\n  %-5s: %s\n  %-5s: %s\n  %-5s: %s\n}\n" \
    "$i" \
    "user" "${w[$i.user]}" \
    "tty"  "${w[$i.tty]}" \
    "from" "${w[$i.from]}"
done

답변4

Bash 배열에 저장하려면 공백 이외의 구분 기호를 사용하는 것이 더 간단한 경우가 많습니다.

    readarray -s2 -t my_w_array < <(w | awk '{ print $1":"$2":"$3 }')

그런 다음 인쇄할 때 분할할 수 있습니다. 예를 들면 다음과 같습니다.

    printf '%s\n' "${my_w_array[@]//:/ }"

관련 정보