%20For%20%E3%83%AB%E3%83%BC%E3%83%97%E3%81%8C%E6%AD%A3%E3%81%97%E3%81%8F%E5%8B%95%E4%BD%9C%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82.png)
test.sh
次の内容を含むbashスクリプトにループがあります。
#!/bin/bash
CHOSEN_NQUEUE=0
foo(){
for chunk in $(seq 0 $((${CHOSEN_NQUEUE}-1)));
do
echo "CHUNK = $(($chunk+1))"
done
}
bar(){
CHOSEN_NQUEUE=10
foo
}
bar
ループはこれまでうまくいきました。プログラムをで実行すると、. test.sh
ループに次のエラーコードが表示されます。
-bash: 0
1
2
3
4
5
6
7
8
9+1: syntax error in expression (error token is "1
2
3
4
5
6
7
8
9+1")
プログラムを次のように実行すると、関数はbash test.sh
目的の結果を生成します。
CHUNK = 1
CHUNK = 2
CHUNK = 3
CHUNK = 4
CHUNK = 5
CHUNK = 6
CHUNK = 7
CHUNK = 8
CHUNK = 9
CHUNK = 10
これは大きなプログラムの一部bash program.sh
です。
特に、単に実行してもfoo
エラーは発生しません。foo
で実行するとbar
エラーが発生します。bash program.sh
これは、使用するかどうかに関係なく発生します. program.sh
。
誰もが私が間違っていることを提案できますか? Bashの他の関数内で関数を実行するのは悪い習慣ですか?
誠にありがとうございます!
編集する:
コメントを残してくれた皆さんに感謝します!
配列でselectを使用して問題が発生したことに気づいた後、次のコードを試しました。
select opt in "${options[@]}"
do
next=false
local IFS=@
case "@${options[*]}@" in
(*"@$opt@"*)
foo
(*)
echo "Invalid option: $REPLY"
;;
esac
echo ""
done
echo "IFS = $IFS"
問題はIFS=@
ループの外側にあるべきではないという事実で発生します。
しかし、このコードを実行してローカルに設定しようとするとIFS
、つまりグローバルに変更されるようlocal IFS=@
です。IFS
コード出力:
IFS = @
なぜこれが起こるのか知っている人がいますか?
もう一度ありがとうございます!
答え1
あなたが受け取ったエラーは、複数$chunk
行の値(すべて0から9までの数字)があることを示します。単語分割が発生すると、これが発生します。いいえ$(seq ...)
結果として発生しますfor
。
単語の区切りを防ぐ一般的な方法は、拡張の周りに二重引用符を入れてfor chunk in "$(seq ...)"
拡張しないようにすることです。しかし、ここではそうではありません。なぜなら、二重引用符を追加すると、とにかく動作することがわかるからです。一部ケース。
しかし、トークン化は常に同じではありません。値に基づいており、デフォルトではIFS
スペース、タブ、および改行($' \t\n'
Cスタイルの引用を使用)が含まれています。他の内容が含まれている場合、その文字は単語区切り文字として扱われます。
実際に、次を呼び出す前にIFS
内部的に修正しました。select
foo
local IFS=@
case "@${options[*]}@" in
(*"@$opt@"*)
foo
Bashで変数の範囲がどのように機能するかは、変更されたfoo
値も表示されることですIFS
。local
これは、変更がこの関数にのみ表示されるのではなく、このレベルで呼び出されるすべてのサブ関数にも表示されることを意味します。
$ x=999
$ a() { echo "a: x=$x"; }
$ b() { local x=$1; a; }
$ b 123
a: x=123
これはC言語の状況とは異なります。
解決策は、IFS
次のように別の変数に保存することです。
local oldifs=$IFS
IFS=@
str="@${options[*]}@"
IFS=$oldifs
case $str in ...
またはサブシェルで変更します(隠されたIFS
変更)。
str=$(IFS=@; echo "@${options[*]}@")
case $str in ...
文字列連結を実行する関数を作成することもできます(関数IFS
の変更を非表示にする)。変数を名前として渡すには、名前参照のみが必要です。
# join a b c:
# join the values of array 'a' with the character 'b', and put the result in 'c'
join() {
local -n _arr=$1
local IFS=$2
local -n _res=$3
_res="${_arr[*]}"
}
src=(11 22 33)
join src @ dst
echo "$dst" # outputs "11@22@33"
(もちろん、これは目的のために少し扱いにくく、名前参照は完璧ではありません。nameref~へ関数は同じ名前の変数を参照できません。外部はい(少なくともBash 4では)。コマンド置換を使用するよりも小さい利点は、サブシェルを起動するためにフォークを使用しないことです。 )
または安全のためにIFS
必要なときはいつでも(再)設定してください。中foo
:
foo() {
local IFS=$' \t\n' # or just IFS=$'\n'
for chunk in $(seq ...); do
...
}