私は私のカメラからファイル名のbashで配列を構築しようとしています:
FILES=(2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg)
ご覧のとおり、各ファイル名の中央にスペースがあります。
各名前を引用符で囲み、スペースをバックスラッシュでエスケープしてみましたが、どちらも機能しません。
配列要素にアクセスしようとすると、スペースが引き続き要素区切り文字として扱われます。
名前の中にスペースがあるファイル名を適切にキャプチャするにはどうすればよいですか?
問題の一部は、要素へのアクセス方法にあると思います。簡単なfor elem in $FILES
を実行すると、同じ問題が発生します。ただし、そのようにインデックスを使用して配列にアクセスする場合、数値またはエスケープを使用して要素を追加すると機能します:
for ((i = 0; i < ${#FILES[@]}; i++))
do
echo "${FILES[$i]}"
done
$FILES
のこれらの宣言はすべて機能するはずです。
FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)
または
FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")
または
FILES[0]="2011-09-04 21.43.02.jpg"
FILES[1]="2011-09-05 10.23.14.jpg"
FILES[2]="2011-09-09 12.31.16.jpg"
FILES[3]="2011-09-11 08.43.12.jpg"
配列のアイテムにアクセスする方法に何か問題があるはずです。方法は次のとおりです。
for elem in "${files[@]}"
...
bash manpage から:
配列の任意の要素は、$ {name [subscript]}を使用して参照できます。 ...添え字が@または*の場合、Wordはnameのすべてのメンバーに展開されます。これらの添え字は、Wordが二重引用符で囲まれている場合にのみ異なります。 Wordが二重引用符で囲まれている場合、$ {name [*]}は、各配列メンバーの値が最初の文字で区切られた単一のWordに展開されますIFS特殊変数の$ {name [@]}は、nameの各要素を個別のWordに展開します。
もちろん、単一のメンバーにアクセスするときは二重引用符も使用する必要があります
cp "${files[0]}" /tmp
要素の区切り文字としてスペースを停止するには、IFSを使用する必要があります。
FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")
IFS=""
for jpg in ${FILES[*]}
do
echo "${jpg}"
done
に基づいて分離する場合。次に、IFS = "。"を実行します。それがあなたを助けることを願っています:)
問題の要素にどのようにアクセスしているかが他の人に同意します。配列の割り当てでファイル名を引用するのは正しいです:
FILES=(
"2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg"
)
for f in "${FILES[@]}"
do
echo "$f"
done
"${FILES[@]}"
という形式の配列を二重引用符で囲むと、配列要素ごとに1つのWordに配列が分割されます。それ以上の単語分割は行いません。
"${FILES[*]}"
の使用にも特別な意味がありますが、結合 $ IFSの最初の文字を持つ配列要素であるため、one Wordになります。あなたが欲しい。
裸の${array[@]}
または${array[*]}
を使用すると、その展開の結果がさらにワード分割されます。そのため、スペースではなくワード(および$IFS
の他のもの)で分割されます。配列要素ごとに1ワード。
Cスタイルのforループを使用しても問題はありません。明確でない場合でも、Wordの分割について心配する必要はありません。
for (( i = 0; i < ${#FILES[@]}; i++ ))
do
echo "${FILES[$i]}"
done
元の質問の引用/エスケープの問題に対する正確な答えではありませんが、おそらく実際にはopにとってより有用なものです:
unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"
もちろん、特定の要件に合わせて式を採用する必要がある場合(例:すべての場合は*.jpg
、特定の日の写真のみの場合は2001-09-11*.jpg
)。
エスケープは機能します。
#!/bin/bash
FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)
echo ${FILES[0]}
echo ${FILES[1]}
echo ${FILES[2]}
echo ${FILES[3]}
出力:
$ ./test.sh
2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg
文字列を引用しても、同じ出力が生成されます。
次のような配列がある場合:#!/ bin/bash
Unix[0]='Debian'
Unix[1]="Red Hat"
Unix[2]='Ubuntu'
Unix[3]='Suse'
for i in $(echo ${Unix[@]});
do echo $i;
done
あなたは得るでしょう:
Debian
Red
Hat
Ubuntu
Suse
理由はわかりませんが、ループはスペースを分解し、個々のアイテムとして配置します。引用符で囲んでもです。
これを回避するには、配列内の要素を呼び出す代わりに、インデックスを呼び出します。インデックスは、引用符で囲まれた完全な文字列を取ります。引用符で囲む必要があります!
#!/bin/bash
Unix[0]='Debian'
Unix[1]='Red Hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'
for i in $(echo ${!Unix[@]});
do echo ${Unix[$i]};
done
次に、取得します:
Debian
Red Hat
Ubuntu
Suse
bash
の使用にとどまっていない場合、ファイル名のスペースの異なる処理は fish Shell の利点の1つです。 「a b.txt」と「b c.txt」の2つのファイルを含むディレクトリを考えます。 bash
を使用して別のコマンドから生成されたファイルのリストを処理する合理的な推測を次に示しますが、ファイル名にスペースが含まれているため失敗します。
# bash
$ for f in $(ls *.txt); { echo $f; }
a
b.txt
b
c.txt
fish
を使用すると、構文はほぼ同じですが、結果は期待どおりです。
# fish
for f in (ls *.txt); echo $f; end
a b.txt
b c.txt
Fishはコマンドの出力をスペースではなく改行で分割するため、動作が異なります。
改行ではなくスペースで分割したい場合は、fish
に非常に読みやすい構文があります。
for f in (ls *.txt | string split " "); echo $f; end
別の解決策は、「for」ループの代わりに「while」ループを使用することです。
index=0
while [ ${index} -lt ${#Array[@]} ]
do
echo ${Array[${index}]}
index=$(( $index + 1 ))
done