Bash - Mảng đa chiều và trích xuất các biến từ đầu ra


8

Tôi đang cố gắng làm một cái gì đó đơn giản tuy nhiên tôi không chắc làm thế nào để đạt được mục tiêu của mình ở đây.

Tôi đang cố gắng trích xuất các giá trị: USER, TTY và TỪ được đưa ra bởi wlệnh trên bàn điều khiển. Trong bash, tôi đang cố gắng đưa đầu ra này và đưa các giá trị này vào một mảng nhiều chiều (hoặc chỉ là một mảng với dấu phân cách không gian).

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

Tôi đã tìm đường đến điểm đọc các giá trị theo từng dòng trong một mảng tuy nhiên tôi dường như không thể lấy giá trị mảng USERS ra khỏi phạm vi của vòng lặp while. Nó in các giá trị 1,2,3,4 và sau đó 0 sau vòng lặp. Mỗi ví dụ tôi đọc họ sử dụng biến ngoài phạm vi hoàn toàn tốt nhưng tôi dường như không thể.


Phía bên phải của một đường ống chạy trong một subshell trong bash, đó là lý do tại sao nó không có sẵn sau vòng lặp.
jordanm

@jordanm vậy không có cách nào để trích xuất mảng USERS?
russ

Sử dụng thay thế quá trình. while read col1 col2 col3 _; do ...; done < <(w)
jordanm

Câu trả lời:


5

Vấn đề chính của bạn là lệnh cuối cùng trong một đường ống chạy trong một lớp con, giống như tất cả các lệnh khác trong đường ống. Đây là trường hợp trong hầu hết các vỏ. ATT ksh và zsh là trường hợp ngoại lệ: chúng chạy lệnh cuối cùng của đường ống trong vỏ cha.

Kể từ bash 4.2, bạn có thể yêu cầu bash hành xử như ksh và zsh bằng cách đặt lastpipe tùy chọn .

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

Ngoài ra, bạn có thể sử dụng thay thế quy trình thay vì đường ống, để readlệnh chạy trong quy trình hệ vỏ chính.

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

Ngoài ra, bạn có thể sử dụng phương pháp di động, hoạt động trong trình bao không có hành vi susbtlation cũng như ksh / zsh, chẳng hạn như Bourne, dash và pdksh. (Bạn vẫn cần (pd) ksh, bash hoặc zsh cho mảng.) Chạy mọi thứ yêu cầu dữ liệu từ đường ống bên trong đường ống.

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

4

Với shopt -s lastpipebạn có thể thực hiện lệnh cuối cùng của một đường ống vào môi trường shell hiện tại. Điều đó giải quyết vấn đề của bạn. Tôi đoán tính năng này không phải lúc nào cũng bị lỗi, vì vậy hãy tránh nó nếu bạn muốn mã tương thích rộng.

Sự thay thế tương thích:

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"

Ý bạn là USERS+=("$line") thay vì USERS[]="$line"?
Runium

@Sukminder Tất nhiên là không. Tôi đã thay thế bởi vì AFAIR, +=ký hiệu đã được thêm vào trong các phiên bản gần đây của bash. Cả hai lệnh đều làm như vậy.
Hauke ​​Laging

4

Mảng Bash là một chiều. Nếu bạn muốn giữ các giá trị riêng biệt theo thứ tự cho mỗi dòng, một giải pháp là sử dụng các mảng kết hợp. Một ví dụ thô thiển:

Ngoài ra, hãy cẩn thận với các tên biến chữ hoa vì chúng có thể xung đột với các biến môi trường.

#!/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

3

để lưu trữ trong các mảng bash, sử dụng một dấu phân cách khác với không gian thường đơn giản hơn.

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

sau đó bạn có thể tách nó khi in nó, như:

    printf '%s\n' "${my_w_array[@]//:/ }"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.