逐行读取文件,将值分配给变量[重复]
Posted
技术标签:
【中文标题】逐行读取文件,将值分配给变量[重复]【英文标题】:Read a file line by line assigning the value to a variable [duplicate] 【发布时间】:2012-06-11 08:50:18 【问题描述】:我有以下 .txt 文件:
Marco
Paolo
Antonio
我想逐行阅读它,并且对于每一行我想将一个 .txt 行值分配给一个变量。假设我的变量是$name
,那么流程是:
$name
= "Marco"
用$name
做一些任务
从文件中读取第二行
分配$name
= "Paolo"
【问题讨论】:
这些问题可以以某种方式合并吗?两者都有一些非常好的答案,突出了问题的不同方面,不好的答案在 cmets 中有深入的解释它们的坏处,到目前为止,你还不能真正从答案中获得关于要考虑什么的完整概述两人的一个问题。将所有内容集中在一个位置会很有帮助,而不是在 2 页上绘制。 【参考方案1】:以下内容逐行读取作为参数传递的文件:
while IFS= read -r line; do
echo "Text read from file: $line"
done < my_filename.txt
这是standard form,用于在循环中从文件中读取行。说明:
IFS=
(或IFS=''
)防止前导/尾随空格被修剪。
-r
防止反斜杠转义被解释。
或者你可以把它放在一个 bash 文件帮助脚本中,示例内容:
#!/bin/bash
while IFS= read -r line; do
echo "Text read from file: $line"
done < "$1"
如果将以上内容保存到文件名为readfile
的脚本中,则可以如下运行:
chmod +x readfile
./readfile filename.txt
如果文件不是standard POSIX text file(= 未由换行符终止),则可以修改循环以处理尾随部分行:
while IFS= read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "$1"
这里,|| [[ -n $line ]]
防止最后一行不以 \n
结尾时被忽略(因为 read
在遇到 EOF 时返回非零退出代码)。
如果循环内的命令也从标准输入中读取,read
使用的文件描述符可能会变成其他东西(避免使用standard file descriptors),例如:
while IFS= read -r -u3 line; do
echo "Text read from file: $line"
done 3< "$1"
(非 Bash shell 可能不知道 read -u3
;请改用 read <&3
。)
【讨论】:
这个方法有个警告。如果 while 循环中的任何内容是交互式的(例如从标准输入读取),那么它将从 $1 获取其输入。您将没有机会手动输入数据。 注意 - 一些命令会破坏(例如,它们会破坏循环)这个。例如,没有-n
标志的ssh
将有效地使您退出循环。这可能是有充分理由的,但在我发现这一点之前,我花了一段时间才确定导致我的代码失败的原因。
单行:while IFS='' read -r line || [[ -n "$line" ]];做回声“$line”;完成
@OndraŽižka,这是由 ffmpeg
使用标准输入引起的。将 </dev/null
添加到您的 ffmpeg
行中,它将无法使用,或者使用备用 FD 进行循环。这种“替代 FD”方法看起来像 while IFS='' read -r line <&3 || [[ -n "$line" ]]; do ...; done 3<"$1"
。
grumble 回复:建议使用.sh
扩展。 UNIX 上的可执行文件通常根本没有扩展名(您不运行ls.elf
),并且有一个 bash shebang(和仅 bash 的工具,如 [[ ]]
)和一个暗示 POSIX sh 兼容性的扩展名在内部是矛盾的。 【参考方案2】:
我鼓励您使用-r
标志来代表read
,它代表:
-r Do not treat a backslash character in any special way. Consider each
backslash to be part of the input line.
我引用man 1 read
。
另一件事是将文件名作为参数。
这里是更新的代码:
#!/usr/bin/bash
filename="$1"
while read -r line; do
name="$line"
echo "Name read from file - $name"
done < "$filename"
【讨论】:
从行中修剪前导和尾随空格 @Thomas 以及中间的空格会发生什么?提示:不需要的命令执行尝试。 这对我有用,与接受的答案相反。 @TranslucentCloud,如果这有效并且接受的答案没有,我怀疑你的外壳是sh
,而不是bash
;接受答案中|| [[ -n "$line" ]]
语法中使用的扩展测试命令是一种bashism。也就是说,该语法实际上具有相关含义:即使没有换行符,它也会导致循环继续输入文件中的最后一行。如果您想以符合 POSIX 的方式执行此操作,则需要 || [ -n "$line" ]
,使用 [
而不是 [[
。
也就是说,这确实仍然需要修改为IFS=
为read
设置read
以防止修剪空白。【参考方案3】:
使用以下 Bash 模板应该允许您一次从文件中读取一个值并对其进行处理。
while read name; do
# Do what you want to $name
done < filename
【讨论】:
作为单行:同时读取名称;做回声 $name;完成 @CalculusKnight,它只是“起作用”,因为您没有使用足够有趣的数据进行测试。尝试使用反斜杠的内容,或仅包含*
的行。
@Matthias,最终证明是错误的假设是最大的错误来源之一,无论是影响安全还是其他方面。我见过的最大的数据丢失事件是由于有人认为“实际上永远不会出现”的场景——缓冲区溢出将随机内存转储到用于命名文件的缓冲区中,导致脚本假设哪些名称可能永远存在碰巧有非常,非常不幸的行为。
@Matthias, ...尤其是在这里,因为 *** 上显示的代码示例旨在用作教学工具,供人们在他们的自己的作品!
@Matthias,我完全不同意“你应该只为你期望的数据设计代码”的说法。意外情况是您的错误所在,您的安全漏洞所在 - 处理它们是草率代码和健壮代码之间的区别。诚然,这种处理不需要花哨——它可以只是“出错退出”——但如果你根本没有处理,那么你在意外情况下的行为是不确定的。【参考方案4】:
#! /bin/bash
cat filename | while read LINE; do
echo $LINE
done
【讨论】:
没有什么反对其他答案的,也许它们更复杂,但我赞成这个答案,因为它简单、易读并且足以满足我的需要。请注意,要使其正常工作,要读取的文本文件必须以空行结尾(即需要在最后一行后按Enter
),否则最后一行将被忽略。至少那是发生在我身上的事情。
无用的猫,shurely ?
并且引用被破坏了;并且你不应该使用大写的变量名,因为它们是为系统使用而保留的。
@AntonioViniciusMenezesMedei,...此外,我看到人们蒙受经济损失,因为他们认为这些警告对他们无关紧要;没有学习好的做法;然后遵循他们在编写管理关键计费数据备份的脚本时的习惯。学会做正确的事很重要。
这里的另一个问题是管道打开了一个新的子shell,即循环结束后所有设置在循环内的变量都无法读取。【参考方案5】:
用途:
filename=$1
IFS=$'\n'
for next in `cat $filename`; do
echo "$next read from $filename"
done
exit 0
如果你设置了不同的IFS
,你会得到奇怪的结果。
【讨论】:
This is a horrible method。请不要使用它,除非您想在您意识到之前遇到通配问题! 这并不可怕,执行过程中没有中断。 @MUYBelgium 您是否尝试使用包含单个*
的文件?无论如何,这是一个反模式。 Don't read lines with for.
@OndraŽižka,read
方法是the best-practices approach by community consensus。您在评论中提到的警告适用于当您的循环运行从标准输入读取的命令(例如ffmpeg
)时,通过使用非标准输入 FD 进行循环或重定向此类命令的输入可以轻松解决。相比之下,解决 for
-loop 方法中的 globbing bug 意味着进行(然后需要反转)shell 全局设置更改。
@OndraŽižka, ...此外,您在此处使用的 for
循环方法意味着必须在循环开始执行之前读入所有内容,如果您“即使您 禁用了通配,也要重新循环数千兆字节的数据; while read
循环一次需要存储不超过一行的数据,这意味着它可以在生成内容的子进程仍在运行时开始执行(因此可用于流式传输),并且还具有有限的内存消耗。跨度>
【参考方案6】:
许多人发布了一个过度优化的解决方案。我不认为这是不正确的,但我谦虚地认为,一个不太优化的解决方案将是可取的,让每个人都可以轻松地理解它是如何工作的。这是我的建议:
#!/bin/bash
#
# This program reads lines from a file.
#
end_of_file=0
while [[ $end_of_file == 0 ]]; do
read -r line
# the last exit status is the
# flag of the end of file
end_of_file=$?
echo $line
done < "$1"
【讨论】:
【参考方案7】:如果您需要同时处理输入文件和用户输入(或来自标准输入的任何其他内容),请使用以下解决方案:
#!/bin/bash
exec 3<"$1"
while IFS='' read -r -u 3 line || [[ -n "$line" ]]; do
read -p "> $line (Press Enter to continue)"
done
基于the accepted answer 和bash-hackers redirection tutorial。
在这里,我们打开作为脚本参数传递的文件的文件描述符 3,并告诉 read
使用此描述符作为输入 (-u 3
)。因此,我们将默认输入描述符 (0) 附加到终端或其他输入源,以便能够读取用户输入。
【讨论】:
如果你想接受管道输入,exec 3<&0
【参考方案8】:
为了正确处理错误:
#!/bin/bash
set -Ee
trap "echo error" EXIT
test -e $FILENAME || exit
while read -r line
do
echo $line
done < $FILENAME
【讨论】:
你能补充一些解释吗? 不幸的是它错过了文件的最后一行。 ...而且,由于缺少引用,包含通配符的 munges 行 - 如BashPitfalls #14 中所述。【参考方案9】:在bash中使用IFS(内部字段分隔符)工具,定义用于将行分隔为标记的字符,默认包含tab> /空格> /新行>
第一步:加载文件数据并插入列表:
# declaring array list and index iterator
declare -a array=()
i=0
# reading file in row mode, insert each line into array
while IFS= read -r line; do
array[i]=$line
let "i++"
# reading from file path
done < "<yourFullFilePath>"
第 2 步:现在迭代并打印输出:
for line in "$array[@]"
do
echo "$line"
done
在数组中回显特定索引:访问数组中的变量:
echo "$array[0]"
【讨论】:
你需要引用你的变量,array[i]="$line"
array[i++]=$line
无需let
【参考方案10】:
以下只会打印出文件的内容:
cat $Path/FileName.txt
while read line;
do
echo $line
done
【讨论】:
这个答案确实没有在现有答案上添加任何内容,由于错字/错误而不起作用,并且在很多方面都中断了。以上是关于逐行读取文件,将值分配给变量[重复]的主要内容,如果未能解决你的问题,请参考以下文章
C 语言文件操作 ( 配置文件读写 | 读取配置文件 | 函数接口形参 | 读取配置文件的逐行遍历操作 | 读取一行文本 | 查找字符 | 删除字符串前后空格 )