为什么这个 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)
作为优化。
要仅在换行符(又名行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
与以前的方法的一个区别是它不会跳过空行。
如果有的话,它还会跳过最后一个换行符之后的字符,但文本文件中不允许这些字符。
它避免将整个文件存储在内存中,但另一方面意味着文件将一次读取一个字节,因为每个文件都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
像 // 那样需要sh
在bash
那里ksh
。
答案2
你说 (在评论中),您想要获取输入文件中的每一行,在前面添加一个 URL,然后在对 的调用中使用它curl
。
可以说最好的方法是在表单上编写一个curl
包含几行的配置文件url
url = http://some/url
然后将该文件传递给 的单次调用curl
。
去做这个:
curl --config <( sed 's|^|url = http://example.com/|' file )
您是否想保存访问每个 URL 的输出,也许保存到某个名为的文件line.out
(line
从文件中读取的行在哪里),您只需要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