Forループとテキストファイルを読む

Forループとテキストファイルを読む

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 feed)にのみ分割するには、同じことを行いますが、引数拡張フラグ(略称)をIFS=$'\n'使用または使用します。fps[\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 - $dataprintf '%s\n' "$data"print -r -- "$data"


1他のPOSIX様シェル(bashなど)とは異なり、zshにはデフォルトで結果がワイルドカードの影響を受ける欠陥がないため、そこにset -o noglob//同じものを持つ必要はありません。shbashksh

答え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で動作します。

関連情報