web-dev-qa-db-ja.com

awkのスペースで区切られた出力を解析できません

私が正しく理解していれば、awkの出力のデフォルトの区切り文字は スペース です。

ただし、次のスクリプトは期待どおりに動作しません。 awkの出力を配列に解析することができません。

#!/bin/bash
echo "------ with input string from awk ------"
ALL_TTY_OWNERS_STR=$(ls -l /dev | grep tty | awk '{print $3}')

read -r -a ALL_TTY_OWNERS_ARRAY <<< "$ALL_TTY_OWNERS_STR"

echo "${#ALL_TTY_OWNERS_ARRAY[@]}" # This says 1
echo "${ALL_TTY_OWNERS_ARRAY[0]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[1]}" # empty string, expected "root"
echo "${ALL_TTY_OWNERS_ARRAY[2]}" # empty string, expected "root"


echo "------ with my manually created input string ------"
ALL_TTY_OWNERS_STR="root root root" # only for testing

read -r -a ALL_TTY_OWNERS_ARRAY <<< "$ALL_TTY_OWNERS_STR"

echo "${#ALL_TTY_OWNERS_ARRAY[@]}" # 3, as expected
echo "${ALL_TTY_OWNERS_ARRAY[0]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[1]}" # "root", as expected
echo "${ALL_TTY_OWNERS_ARRAY[2]}" # "root", as expected

awkの出力をreadで解析できないのはなぜですか?

4
Gauthier

それは約フィールドセパレーターです。

各文字列を1つにまとめるには、recordセパレータを定義する必要があります。 ORS paramを使用します:

ls -l /dev | grep tty | awk 'BEGIN { ORS=" " }; {print $3}'

それがないと、次のように出力されます。

root
root
root
etc...

そして、ALL_TTY_OWNERS_STR変数を定義すると、最初にのみ置く配列の最初の要素の出力の文字列。このため、配列には1つの要素のみが含まれ、これがまさにあなたが得ているものです。

5
Egor Vasilyev

出力のデフォルトのフィールド区切り文字はスペースです。ただし、レコード区切り文字は改行です。 ALL_TTY_OWNERS_STRには、改行で区切られたrootsの束が含まれています。

$ printf "%q\n" "$ALL_TTY_OWNERS_STR"
$'root\nroot\nroot\n....

そして、readはデフォルトで、最初の改行まで読み取ります。

ALL_TTY_OWNERS_ARRAYのすべてのユーザーが必要な場合は、次のようにする方が簡単な場合があります。

ALL_TTY_OWNERS_ARRAY=( $(stat -c '%U' /dev/*tty*) )
4
muru

read -a arrayは、1つのレコードからすべての単語をarrayに読み込みます。

入力全体からすべての単語を読み取りたい場合は、入力で発生しないレコード区切り文字を使用します。たとえば、レコード区切り文字としてNUL文字を使用する-d ''、または-d ::はユーザー名では使用できません)。

IFS=$'\n' read -rd '' -a array < <(ls -Ll /dev | awk '/tty/{print $3}')

(ここでは-Lを使用して、シンボリックリンクの場合、シンボリックリンクではなくデバイスの所有権が返されるようにします)。

または、配列に格納される入力のlineごとにreadarrayを使用します。

readarray -t array < <(ls -Ll /dev | awk '/tty/{print $3}')

または、split + glob演算子を使用できます。

set -o noglob # disable glob part
IFS=$'\n'     # split on newline
array=($(ls -Ll /dev | awk '/tty/{print $3}')) # invoke split+glob by leaving
                                               # the $(...) unquoted.

ttyls -Llの出力全体で検索されるため、ユーザー名とグループ名、ファイル名(および、-Lを省略した場合はシンボリックリンクのターゲット)が含まれることに注意してください。ファイル名のみを検討したい場合は、次のことができます。

ls -Lld /dev/*tty* | awk '{print $3}'

代わりに。

zshを使用すると、次のことができます。

zmodload zsh/stat
stat -s -A array +uid /dev/*tty*

(これには、空白を含むユーザー名に対しても機能するという利点があります)。

3

<<<の後に変数を引用符で囲まない場合、改行は期待する空白になり、結果はすべて1行になります。

read -r -a ALL_TTY_OWNERS_ARRAY <<< $ALL_TTY_OWNERS_STR
0
meuh