Bashのテキストファイルから配列を作成する


88

スクリプトはURLを受け取り、それを解析して必須フィールドを探し、その出力をリダイレクトしてファイルfile.txtに保存します。フィールドが見つかるたびに、出力が新しい行に保存されます。

file.txt

A Cat
A Dog
A Mouse 
etc... 

file.txt新しいスクリプトで配列を取得して作成したいと思います。このスクリプトでは、すべての行が配列内の独自の文字列変数になります。これまで私が試した:

#!/bin/bash

filename=file.txt
declare -a myArray
myArray=(`cat "$filename"`)

for (( i = 0 ; i < 9 ; i++))
do
  echo "Element [$i]: ${myArray[$i]}"
done

このスクリプトを実行すると、空白が原因で単語が分割され、

必要な出力

Element [0]: A Cat 
Element [1]: A Dog 
etc... 

私はこれを取得することになります:

実際の出力

Element [0]: A 
Element [1]: Cat 
Element [2]: A
Element [3]: Dog 
etc... 

各行の文字列全体が配列内の各変数と1対1で対応するように、以下のループを調整するにはどうすればよいですか?


5
これがBashFAQ001のすべてです。また、Bash FAQ005の配列トピックのこのセクション
Etan Reisner

1
私はこれをstackoverflow.com/questions/11393817/…の複製としてリンクしますが、受け入れられた答えはひどいものです。
Charles Duffy

エタン、こんなに早くて正確な返事をありがとう!フォーラムで質問を検索しようとしましたが、stackoverflowに関するFAQを探すことは考えていませんでした。mapfileコマンドは私のニーズに正確に対応しました!もう一度ありがとう:)セクション2.1で答えてください。
user2856414 2015年

2
(ここにあるよりも受け入れられた答えがあるので、反対方向にリンクを設定します)。
チャールズダフィー

回答:


110

次のmapfileコマンドを使用します。

mapfile -t myArray < file.txt

エラーは使用していますfor-ファイルのをループする慣用的な方法は次のとおりです。

while IFS= read -r line; do echo ">>$line<<"; done < file.txt

詳細については、BashFAQ / 005を参照してください。


5
これは正規のQ&Aとして宣伝されているため、リンクに記載されている内容を含めることもできますwhile IFS= read -r; do lines+=("$REPLY"); done <file
fedorqui'SOは害をやめる '2016

10
mapfileは、4.xより前のbashバージョンには存在しません
ericslaw 2017年

14
Bash4は現在約5歳です。アップグレード。
glenn jackman 2017年

5
2009年にbash4がリリースされたにもかかわらず、@ ericslawのコメントは関連性があります。多くのマシンにはまだbash3.xが付属しているためです(bashがGPLv3でリリースされている限り、アップグレードされません)。あなたが移植に興味があるなら、それは注意することが重要なことだ
デノボ

12
問題は、開発者がアップグレードされたバージョンをインストールできないことではありません。開発者は、を使用mapfileするスクリプトが追加の手順なしで多くのマシンで期待どおりに実行されないことに注意する必要があります。@ericslaw macsは、当面の間、bash3.2.57とともに出荷され続けます。最近のバージョンでは、Appleが共有または許可したくないものを共有または許可する必要があるライセンスを使用しています。
デ・ノボ

24

mapfileおよびreadarray(同義語)は、Bashバージョン4以降で使用できます。古いバージョンのBashを使用している場合は、ループを使用してファイルを配列に読み込むことができます。

arr=()
while IFS= read -r line; do
  arr+=("$line")
done < file

ファイルの最後の行が不完全(改行がない)の場合は、次の方法を使用できます。

arr=()
while IFS= read -r line || [[ "$line" ]]; do
  arr+=("$line")
done < file

関連:


それが機能するためには、括弧を付けなければならないことがわかりましたIFS= read -r line || [[ "$line" ]]。そうでなければ、それはうまくいきます!
TatianaRacheva20年

@TatianaRacheva:以前に欠落していたセミコロンではありませdoんか?
codeforester

9

あなたもこれを行うことができます:

oldIFS="$IFS"
IFS=$'\n' arr=($(<file))
IFS="$oldIFS"
echo "${arr[1]}" # It will print `A Dog`.

注意:

ファイル名の展開は引き続き発生します。たとえば、リテラルを含む行がある*場合、現在のフォルダ内のすべてのファイルに展開されます。したがって、ファイルにこの種のシナリオがない場合にのみ使用してください。


IFS割り当てを保持しながら、一時的にのみ設定する方法はありますか(このコマンドの後に元の値を回復するため)arr
Hugues 2015

1
ファイル名の展開は引き続き発生することに注意してください。例IFS=$'\n' arr=($(echo 'a 1'; echo '*'; echo 'b 2')); printf "%s\n" "${arr[@]}"
Hugues 2015

@Hugues:うん、ファイル名の拡張はまだ発生します。私は.. info..thnksのそのビットが追加されます
Jahid

申し訳ありませんが、同意しません。 現在のシェルではIFS=... command変更さIFSれません。ただし、IFS=... other_variable=...(コマンドなしで)現在のシェルIFSとの両方が変更さother_variableれます。
Hugues

1
ありがとう!これは機能します。arr=mapfile/と比較してreadarray)表記が好きなので、これ以上簡単な方法がないのは残念です。
Hugues 2015

4

ファイルから各行を読み取り、配列に割り当てるだけです。

#!/bin/bash
i=0
while read line 
do
        arr[$i]="$line"
        i=$((i+1))
done < file.txt

1
アレイにどのようにアクセスしますか?
hola 2018年

4

mapfileを使用するか、-aを読み取ります

常にshellcheckを使用してコードをチェックしてください。それはしばしばあなたに正しい答えを与えるでしょう。この場合、SC2207は、スペース区切りまたは改行区切りの値を持つファイルの配列への読み取りを対象としています。

これをしないでください

array=( $(mycommand) )

値が改行で区切られたファイル

mapfile -t array < <(mycommand)

値がスペースで区切られているファイル

IFS=" " read -r -a array <<< "$(mycommand)"

シェルチェックページには、これがベストプラクティスと見なされる理由が記載されています。


0

この答えは使用するように言います

mapfile -t myArray < file.txt

なんらかの理由でbash <4.xでmapfile使用mapfileしたい場合のためにシムを作成しました。mapfilebash> = 4.xを使用している場合は、既存のコマンドを使用します

現在、オプション-d-t動作のみ。しかし、上記のコマンドにはそれで十分です。私はmacOSでのみテストしました。macOS Sierra 10.12.6では、システムbashは3.2.57(1)-releaseです。そのため、シムが重宝します。bashを自作で更新したり、自分でbashを作成したりすることもできます。

この手法を使用して、1つの呼び出しスタックに変数を設定します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.