次のような bash 関数があり、それが私を困らせています。zenity ボックスに次のように入力すると...
従業員ID = 2 カテゴリID = 3
次のような結果が出ました: 2 3
もし...
従業員ID = カテゴリID = 3
2番目のZenityウィンドウが開いたら、2と入力すると次のようになります: 2 3
しかし、私が入ると
従業員ID = 2 カテゴリID =
追加のZenityウィンドウは開かず、次のメッセージが表示されます: 2
私が本当に望んでいるのは、テストを実行した後の 2、3 です。
ここで何が問題なのか誰か知っていますか?
#!/bin/bash
num(){
emp=$(echo "$1" | awk -F, -v OFS=, '{print $1 "," $2}')
IFS=, read -ra array1 <<<"$emp"
p=$(for i in "${array1[@]}"
do
if [[ "${i}" =~ ^[0-9]+$ ]]; then
out="${i}"
elif
[[ "${i}" = NULL ]]; then
out="${i}"
else local var2
until [[ ${var2} =~ ^[0-9]+$ ]] || [[ ${var2} = NULL ]]; do
var2="$(zenity --forms --title="table salaries_wages" --text "Add a number" --separator="," \
--add-entry="WARNING! You either forgot to enter or didn't enter a number. Please enter a valid number: ")"
done
out="${var2}"
fi
echo "$out"
done)
echo "$p"
}
input="$(zenity --forms --title="table salaries_wages" --text="Add a new salaries_wages entry" --separator="," \
--add-entry="ENTER employeeid: " \
--add-entry="ENTER categoryid: ")"
num "$input"
答え1
ここで何が問題なのですか?
どのように動作するかという想定は、実際にread
どのように動作するかとは異なりますread
。次のコードを Bash で実行します。
how_many () { IFS=, read -ra array1 <<<"$1"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"
2
、、が表示されます。最後の数字が目立ちます。これは、末尾の区切り文字 (この場合は) がターミネータのように扱われることを意味します2
。つまり、その後の空のフィールドは配列に読み込まれず、配列の要素が 1 つ短くなります。コード内でこれが発生すると、最初のフィールドに対してのみループが実行されます。1
,
read
for i in "${array1[@]}"
解決策としては、余分な ,
を末尾のターミネータとして意図的に使用します。すると、read
3 番目のフィールドは読み込まれませんが、常に 2 つのフィールドが読み込まれます (2 番目のフィールドが空であっても)。 を追加した場合の違いを確認してください,
。
how_many () { IFS=, read -ra array1 <<<"$1,"; echo "${#array1[@]}"; }
how_many "2,3"
how_many ",3"
how_many "2,"
how_many ","
出力は2
毎回です。
この方法でコードを修正するには、<<<"$emp,"
の代わりにを使用します<<<"$emp"
。
コードを修正すると、else
ブロックが複数回実行されると(つまり、両方のフィールドが最初は無効である場合)、動作がおかしくなります。var2
変数の再利用また。
以前はこれを避けていたと思いますlocal var2
が、ブロック内やループの単一の反復内local
ではなく、関数内で変数をローカルにします。関数の同じインスタンスで再利用しています。else
for
var2
num
関数を一度呼び出しています。内部はvar2
常に同じ でvar2
、関数の呼び出しの外部local
と区別されるだけです。関数の外部で を使用した場合、関数内の は異なります。複数回呼び出した場合、各呼び出しで独自の が使用されます。これらのどちらも発生しません。関数を一度呼び出し、変数を再利用しています。var2
var2
num
var2
そこには2つのフィールドが無効な場合、最初のフィールドに変数が使用され、次に再利用2番目のフィールド用。
validate
しかし、ループ内から何らかの関数 (例 ) が呼び出されるようにコードを再構築すると、次のようになります。
for i in "${array1[@]}"; do validate "$i"; …
local var2
関数内で使用すればvalidate
、var2
関数の各呼び出しは別々になります。これがlocal
役に立つ方法です。各ループでvalidate
新たに呼び出され、そのローカル変数は同じ名前の他の変数との接続なしに新しく初期化されます。ただし、ローカル変数を再利用できます。内部関数を、何かを壊すような方法で再利用します (現在関数var2
内で再利用している方法と同様num
)。
上記を書いた後、すでにリンクされている回答。
はp=$(stuff); echo "$p"
とほぼ同等でありecho "$(stuff)"
、ほとんどの場合 と等価ですstuff
。この答えについて詳しく説明されていますvar=$(stuff); echo "$var"
。