これは、カーリング https://unix.stackexchange.com/ で、結果を配列に格納する単純なスクリプトです。これは正常に機能しています。
#!/usr/local/bin/bash
[ -f pgtoscrap ] && { rm pgtoscrap; };
curl -o pgtoscrap https://unix.stackexchange.com/;
declare -a arr;
fileName="pgtoscrap";
exec 10<&0
exec < $fileName
let count=0
while read LINE; do
arr[$count]=$LINE
((count++))
done
exec 0<10 10<&-
しかし、このスクリプトを実行するたびに。間違ったファイル記述子でエラーが発生します。
./shcrap
./shcrap: line 14: 10: No such file or directory
ループ内でexec
コマンドを正しく使用する方法がよくわからないと思います。誰かが説明できますか?
-Bash4にmapfile
を実装した後の更新は、はるかに簡単になりました-
#!/usr/local/bin/bash
## Pass a parameter as e.g. ./linkscrapping.bash https://unix.stackexchange.com/
mapfile -t arr < <(curl -s $1); ## Doing exec stuff with process substitution
regex="<a[[:print:]]*<\/a>"; ELEMENTS=${#arr[@]}; firstline=0;
for((i=0;i<$ELEMENTS;i++)); do
if [[ ${arr[${i}]} =~ $regex ]]; then
[[ $firstline<1 ]] &&
{ echo ${BASH_REMATCH[0]} > scrapped; let firstline=$firstline+1; } ||
{ echo ${BASH_REMATCH[0]} >> scrapped; }
fi
done
pg2scrap="scrapped"; mapfile -t arr2 < <(cat $pg2scrap);
regex="href=[\"\'][0-9a-zA-Z\:\/\.]+"; ELEMENTS2=${#arr2[@]}; line2=0
for ((i=0;i<$ELEMENTS2;i++)); do
if [[ ${arr2[${i}]} =~ $regex ]]; then
[[ $line2<1 ]] &&
{ echo ${BASH_REMATCH[0]#href=\"} > links; (( line2++ )); } ||
{ echo ${BASH_REMATCH[0]#href=\"} >> links; }
fi
done; cat links;
それは確かに、stdin用に以前に開いたファイル記述子を閉じる方法と関係があります。以下を使用しても問題ありません
_exec 10<&-
_
_0<10
_を実行するときは、現在のディレクトリにある_10
_という名前のファイルの内容を検索してSlurpするようにシェルに指示します。これは、このコンテキストでは意味がありますno。
bash
では、記述子を閉じるのと同じ目的を達成する別の形式_exec 10>&-
_を使用することもできます。
ただし、ランダムファイル記述子でexec
を使用して入力を読み取る必要はありません。< <()
なので
_while IFS= read -r line; do
arr["$count"]="$line"
((count++))
done< <(pgtoscrap)
_
exec 10<&0
は、ファイル記述子番号0から番号10のクローンを作成し、元のファイルを効果的に保存して、次の行のfd0のファイルを置き換えることができるようにします。これを元に戻すには、番号を逆にして、番号10を番号0に複製する必要があります。exec 0<&10
(次に、fd10をexec 10<&-
で閉じます)。
一方、アンパサンドのないexec 0<10
は、ファイル名10
を使用した単なるリダイレクトです。そのようなファイルがないため、エラーが発生します。
とはいえ、whileループのリダイレクトを一時的に設定するためにexec
を使用する必要はありません。複合コマンドは、次のようにリダイレクトすることもできます。
while read LINE; do
...
done < "$filename"
空白やバックスラッシュがデータに影響を与えずに、行全体をそのまま読み取りたい場合は、IFS
のread
の設定を解除し、read -r
を使用する必要があります。また、配列に追加する場合は、手動でインデックスに対応する必要はありません。+=
を使用して配列に直接追加できます。
arr=() # declares it an array and clears it, not strictly necessary though
while IFS= read -r line; do
arr+=("$line")
done < "$filename"
または、コメントで@BlackJackが言及しているような手動ループの代わりに、mapfile
(readarray
)を使用します。
mapfile -t arr < "$filename"
または、一時ファイルがまったくない場合でも:
#/bin/bash
mapfile -t arr < <(curl -s https://unix.stackexchange.com/)
(-t
がない場合、mapfile
はラインターミネータをそのまま残します。)