
forループが期待どおりに機能しないのはなぜですか?
私のスクリプトファイルは次のとおりです(Ventura OS 13.2.1がインストールされているMACコンピュータでzshを実行しています)。
#!/bin/zsh
for user in "$(cat $1)";
do
website="myWebSite.org/@/"
url=$website$user
echo "here is the url: $url"
done
ここに私のデータファイルがあります。 1行に1つずつ4つのユーザー名があります。
cat userList.txt
user_1
user_2
user_3
user_4
私は出力が次のようになると思います。
myWebSite.org/@/user_1
myWebSite.org/@/user_2
myWebSite.org/@/user_3
myWebSite.org/@/user_4
代わりに私が得た結果は次のとおりです。
./findusers userList.txt
here is the url: myWebSite.org/@/user_1
user_2
user_3
user_4
私は数時間インターネット検索をしてきましたが、この種の問題に近いものが見つかりませんでした。シェルが私が見ることができないいくつかのファイルを操作するのとほとんど似ており、forループのechoコマンドが一度だけ実行される理由を理解できません。私のシステムにある古いファイルのバージョンの問題ですか?助けてくれてありがとう。 zshコーディングは私の本業ではありません。ありがとうございます!
答え1
コマンド置換を分割する必要があります。
for user in $(<$1)
引用符がない場合、デフォルトは$(...)
空白、タブ、改行、およびヌル文字に分割されます。 Kornに似た演算子は、ここでは代わりに最適化として$IFS
使用されます。$(<file)
$(cat -- $1)
改行(別名line f
eed)にのみ分割するには、同じことを行いますが、引数拡張フラグ(略称)をIFS=$'\n'
使用または使用します。f
ps[\n]
for user in ${(f)"$(<$1)"}
IFS分割を避けるために引用符を記録し、改行文字からf
分割するフラグを記録します。
while read
ループを使用することもできます。
while IFS= read -ru3 user; do
...
done 3< $1
以前の方法との違いの1つは、空行をスキップしないことです。
また、最後の改行の後に文字がある場合はスキップされますが、これらの文字はテキストファイルでは許可されません。
ファイル全体がメモリに保存されるのを防ぎますが、各ファイルは行を区切るread
改行文字以上を読み取らないようにする必要があるため、ファイルを一度に1バイトずつ読みます。
そして:
for user in "${(f@)$(<$1)}"
または:
IFS=$'\n\n'
for user in $(<$1)
コマンド置換バーとして空白行(末尾の空白行を除く)を残します。みんな末尾の改行文字。
すべての行を配列として読み取ると、空行と最後の改行(ある場合)の後のバイトで構成される非行を考慮し、それを繰り返すことは非常にぎこちないので、ヘルパー関数を使用できます。
lines() {
local ret
reply=( "${(@f)$(cat -- "$@"; ret=$?; echo .; exit $ret)}" )
ret=$?
reply[-1]=( ${reply[-1]%.} )
return $ret
}
lines myfile &&
for line in "$reply[@]"; do
something with "$line"
done
また、echo
任意のデータ出力を避ける必要があります(実際にはzsh
使用できますが)、他のシェルやKornシェルと同様に使用するのが最善ですecho -E - $data
。printf '%s\n' "$data"
print -r -- "$data"
1他のPOSIX様シェル(bashなど)とは異なり、zshにはデフォルトで結果がワイルドカードの影響を受ける欠陥がないため、そこにset -o noglob
//同じものを持つ必要はありません。sh
bash
ksh
答え2
あなたが言う(コメントから)、入力ファイルの各行を取得し、URLの前にURLを追加してから呼び出しに使用しようとしていますcurl
。
curl
おそらく最良の方法は、フォームに数行で構成ファイルを作成することです。url
url = http://some/url
次に、そのファイルを単一の呼び出しに渡しますcurl
。
これを行うには:
curl --config <( sed 's|^|url = http://example.com/|' file )
line.out
訪問した各URLの出力を名前付きファイル(line
ファイルから読み取った行がある場所)に保存するには、URLごとoutput
に1つのステートメントを挿入するだけです。
GNUの使用方法は次のとおりですsed
。
curl --config <( sed 's|.*|url = http://example.com/&\noutput = &.out|' file )
または以下を使用してくださいawk
。
curl --config <( awk '{ printf "url = http://example.com/%s\noutput = %s.out\n", $0, $0 }' file )
最後の2つのコマンドは、入力ファイルの行に単純な単語が含まれていることを知っていると仮定します。文字列に絶対パス名または相対パス名またはcurl
ユーティリティ固有のパターンが含まれている場合は、最初に削除する必要があります。
答え3
また、forループからファイルを読み取る問題が発生しました。
これにはwhileループを使用します。
FILE="some_items.txt"
LINES=$(cat $FILE|wc -l)
INDEX=0
while [ $INDEX -lt $LINES ]
do
LN=$((INDEX + 1))
ITEM="$(cat $FILE|head -n $LN|tail -n 1)"
echo "current item is $ITEM"
INDEX=$((INDEX + 1))
done
これはzsh、bash、shで動作します。