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

这是我的数据文件,它有四个用户名,每一行一个:

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

我已经用谷歌搜索了几个小时,但找不到任何接近此类问题的内容。这几乎就像 shell 正在操作一些我看不到的文件,我不明白为什么 for 循环中的 echo 命令只执行一次。这是我系统上陈旧文件的版本问题吗?感谢任何帮助,在 zsh 中编码不是我的日常工作,谢谢!

答案1

您需要拆分该命令替换。

for user in $(<$1)

不带引号时,默认按空格、制表符、换行符和 nul$(...)字符进行分割。$IFS这里使用$(<file)类似 Korn 的运算符而不是$(cat -- $1)作为优化。

要仅在换行符(又名行feed)上分割,请执行相同的操作,但在之后IFS=$'\n'或使用f参数扩展标志(缩写为ps[\n]):

for user in ${(f)"$(<$1)"}

请注意引号以防止 IFS 拆分,然后注意f要在换行符上拆分的标志。

您还可以使用while read循环:

while IFS= read -ru3 user; do
  ...
done 3< $1

与以前的方法的一个区别是它不会跳过空行。

如果有的话,它还会跳过最后一个换行符之后的字符,但文本文件中不允许这些字符。

它避免将整个文件存储在内存中,但另一方面意味着文件将一次读取一个字节,因为每个文件都read需要确保它不会读取超过分隔行的换行符。

和:

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,您实际上可以使用),最好像在任何其他 shell 或Korn shell 中一样echo -E - $data使用。printf '%s\n' "$data"print -r -- "$data"


1 请注意,与其他类似 POSIX 的 shell(例如 bash)相反,zsh 默认情况下没有这样的缺陷,即结果会进一步受到通配符的影响,因此您不需要set -o noglob像 // 那样需要shbash那里ksh

答案2

你说 (在评论中),您想要获取输入文件中的每一行,在前面添加一个 URL,然后在对 的调用中使用它curl

可以说最好的方法是在表单上编写一个curl包含几行的配置文件url

url = http://some/url

然后将该文件传递给 的单次调用curl

去做这个:

curl --config <( sed 's|^|url = http://example.com/|' file )

您是否想保存访问每个 URL 的输出,也许保存到某个名为的文件line.outline从文件中读取的行在哪里),您只需要output为每个 URL 插入一条语句。

以下是使用 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 )

请注意,最后两个命令假设我们知道输入文件中的行包含简单的单词。如果字符串包含绝对或相对路径名,或者特定于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

相关内容