Linux 在 Bash 中循环遍历文件的内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1521462/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:45:35  来源:igfitidea点击:

Looping through the content of a file in Bash

linuxbashloopsunixio

提问by Peter Mortensen

How do I iterate through each line of a text file with Bash?

如何使用Bash遍历文本文件的每一行?

With this script:

使用这个脚本:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

I get this output on the screen:

我在屏幕上得到这个输出:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Later I want to do something more complicated with $pthan just output to the screen.)

(后来我想做一些更复杂的事情,$p而不仅仅是输出到屏幕。)



The environment variable SHELLis (from env):

环境变量SHELL是(来自 env):

SHELL=/bin/bash

/bin/bash --versionoutput:

/bin/bash --version输出:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/versionoutput:

cat /proc/version输出:

Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

The file peptides.txt contains:

文件peptides.txt 包含:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

采纳答案by Bruno De Fraine

One way to do it is:

一种方法是:

while read p; do
  echo "$p"
done <peptides.txt

As pointed out in the comments, this has the side effects of trimming leading whitespace, interpreting backslash sequences, and skipping the last line if it's missing a terminating linefeed. If these are concerns, you can do:

正如评论中指出的那样,这具有修剪前导空格、解释反斜杠序列以及在缺少终止换行符时跳过最后一行的副作用。如果有这些问题,您可以这样做:

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt


Exceptionally, if the loop body may read from standard input, you can open the file using a different file descriptor:

例外的是,如果循环体可以从标准输入读取,您可以使用不同的文件描述符打开文件:

while read -u 10 p; do
  ...
done 10<peptides.txt

Here, 10 is just an arbitrary number (different from 0, 1, 2).

这里,10 只是一个任意数字(不同于 0、1、2)。

回答by Warren Young

cat peptides.txt | while read line 
do
   # do something with $line here
done

and the one-liner variant:

和单线变体:

cat peptides.txt | while read line; do something_with_$line_here; done

These options will skip the last line of the file if there is no trailing line feed.

如果没有尾随换行,这些选项将跳过文件的最后一行。

You can avoid this by the following:

您可以通过以下方式避免这种情况:

cat peptides.txt | while read line || [[ -n $line ]];
do
   # do something with $line here
done

回答by Stan Graves

Option 1a:While loop: Single line at a time: Input redirection

选项 1a:While 循环:一次一行:输入重定向

#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do 
    echo $p
done < $filename

Option 1b:While loop: Single line at a time:
Open the file, read from a file descriptor (in this case file descriptor #4).

选项 1b:While 循环:一次一行:
打开文件,从文件描述符中读取(在本例中为文件描述符 #4)。

#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
    echo $p
done

回答by mightypile

This is no better than other answers, but is one more way to get the job done in a file without spaces (see comments). I find that I often need one-liners to dig through lists in text files without the extra step of using separate script files.

这并不比其他答案好,但它是另一种在没有空格的文件中完成工作的方法(见评论)。我发现我经常需要单行代码来挖掘文本文件中的列表,而无需使用单独的脚本文件的额外步骤。

for word in $(cat peptides.txt); do echo $word; done

This format allows me to put it all in one command-line. Change the "echo $word" portion to whatever you want and you can issue multiple commands separated by semicolons. The following example uses the file's contents as arguments into two other scripts you may have written.

这种格式允许我将所有内容放在一个命令行中。将“echo $word”部分更改为您想要的任何内容,您可以发出多个由分号分隔的命令。以下示例使用文件内容作为您可能编写的其他两个脚本的参数。

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done

Or if you intend to use this like a stream editor (learn sed) you can dump the output to another file as follows.

或者,如果您打算像流编辑器一样使用它(学习 sed),您可以将输出转储到另一个文件,如下所示。

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt

I've used these as written above because I have used text files where I've created them with one word per line. (See comments) If you have spaces that you don't want splitting your words/lines, it gets a little uglier, but the same command still works as follows:

我使用上面写的这些是因为我使用了文本文件,其中每行一个单词。(请参阅评论)如果您有不想拆分单词/行的空格,它会变得更难看,但相同的命令仍然可以按如下方式工作:

OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

This just tells the shell to split on newlines only, not spaces, then returns the environment back to what it was previously. At this point, you may want to consider putting it all into a shell script rather than squeezing it all into a single line, though.

这只是告诉 shell 只在换行符上拆分,而不是空格,然后将环境返回到以前的状态。在这一点上,您可能需要考虑将其全部放入一个 shell 脚本中,而不是将其全部压缩到一行中。

Best of luck!

祝你好运!

回答by Sine

#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done

回答by Jahid

Use a while loop, like this:

使用 while 循环,如下所示:

while IFS= read -r line; do
   echo "$line"
done <file

Notes:

笔记:

  1. If you don't set the IFSproperly, you will lose indentation.

  2. You should almost always use the -r option with read.

  3. Don't read lines with for

  1. 如果您没有IFS正确设置,您将失去缩进。

  2. 您几乎应该总是将 -r 选项与 read 一起使用。

  3. 不要阅读行 for

回答by Whome

Here is my real life example how to loop lines of another program output, check for substrings, drop double quotes from variable, use that variable outside of the loop. I guess quite many is asking these questions sooner or later.

这是我的现实生活示例,如何循环另一个程序输出的行、检查子字符串、从变量中删除双引号、在循环外使用该变量。我想很多人迟早会问这些问题。

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

Declare variable outside of the loop, set value and use it outside of loop requires done <<< "$(...)"syntax. Application need to be run within a context of current console. Quotes around the command keeps newlines of output stream.

在循环外声明变量,设置值并在循环外使用它需要done <<< "$(...)"语法。应用程序需要在当前控制台的上下文中运行。命令周围的引号保留输出流的换行符。

Loop match for substrings then reads name=valuepair, splits right-side part of last =character, drops first quote, drops last quote, we have a clean value to be used elsewhere.

子字符串的循环匹配然后读取name=value对,拆分 last =字符的右侧部分,删除第一个引号,删除最后一个引号,我们有一个干净的值可以在其他地方使用。

回答by Alan Jebakumar

@Peter: This could work out for you-

@Peter:这对你有用-

echo "Start!";for p in $(cat ./pep); do
echo $p
done

This would return the output-

这将返回输出 -

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

回答by dawg

Suppose you have this file:

假设你有这个文件:

$ cat /tmp/test.txt
Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR

There are four elements that will alter the meaning of the file output read by many Bash solutions:

有四个元素会改变许多 Bash 解决方案读取的文件输出的含义:

  1. The blank line 4;
  2. Leading or trailing spaces on two lines;
  3. Maintaining the meaning of individual lines (i.e., each line is a record);
  4. The line 6 not terminated with a CR.
  1. 空行4;
  2. 两行的前导或尾随空格;
  3. 维护各行的含义(即每一行都是一条记录);
  4. 第 6 行不以 CR 终止。

If you want the text file line by line including blank lines and terminating lines without CR, you must use a while loop and you must have an alternate test for the final line.

如果您希望文本文件逐行包括空行和没有 CR 的终止行,则必须使用 while 循环,并且必须对最后一行进行备用测试。

Here are the methods that may change the file (in comparison to what catreturns):

以下是可能更改文件的方法(与cat返回的内容相比):

1) Lose the last line and leading and trailing spaces:

1) 丢掉最后一行和前导和尾随空格:

$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'

(If you do while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txtinstead, you preserve the leading and trailing spaces but still lose the last line if it is not terminated with CR)

(如果你这样做while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt,你会保留前导和尾随空格,但如果最后一行没有以 CR 终止,则仍然会丢失最后一行)

2) Using process substitution with catwill reads the entire file in one gulp and loses the meaning of individual lines:

2) 使用进程替换 with catwill 一口气读完整个文件并失去个别行的含义:

$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR'

(If you remove the "from $(cat /tmp/test.txt)you read the file word by word rather than one gulp. Also probably not what is intended...)

(如果您删除"$(cat /tmp/test.txt)您读取单词文件字,而不是一口。而且可能不是什么意...)



The most robust and simplest way to read a file line-by-line and preserve all spacing is:

逐行读取文件并保留所有间距的最可靠和最简单的方法是:

$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'    Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space    '
'Line 6 has no ending CR'

If you want to strip leading and trading spaces, remove the IFS=part:

如果要剥离前导和交易空间,请删除该IFS=部分:

$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'

(A text file without a terminating \n, while fairly common, is considered broken under POSIX. If you can count on the trailing \nyou do not need || [[ -n $line ]]in the whileloop.)

(没有终止的文本文件\n,而相当普遍,被认为是POSIX断下。如果你能在后指望\n你不需要|| [[ -n $line ]]while循环。)

More at the BASH FAQ

BASH 常见问题解答中的更多信息

回答by Anjul Sharma

If you don't want your read to be broken by newline character, use -

如果您不希望您的阅读被换行符破坏,请使用 -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < ""

Then run the script with file name as parameter.

然后以文件名作为参数运行脚本。