Bash-遍历输出行

Bash-遍历输出行,bash,Bash,我想要什么:找到所有nginx访问日志文件,迭代它们(从中获取一些数据) 我被困在循环中: #!/bin/bash logfiles="$(find /var/log/nginx -name 'access.log*')" for lf in "$logfiles" do echo "file" done 输出只有一个“文件”字,尽管有多个日志文件。怎么了?将这一行中的引号丢失:用于$logfiles中的lf 但是看起来您可能只有一个名为access.log的文件丢失这一行中的引号

我想要什么:找到所有nginx访问日志文件,迭代它们(从中获取一些数据)

我被困在
循环中:

#!/bin/bash

logfiles="$(find /var/log/nginx -name 'access.log*')"

for lf in "$logfiles"
do
    echo "file"
done

输出只有一个“文件”字,尽管有多个日志文件。怎么了?

将这一行中的引号丢失:
用于$logfiles中的lf

但是看起来您可能只有一个名为access.log的文件

丢失这一行中的引号:
for lf in$logfiles
但是当你说的时候,看起来你可能只有一个名为access.log的文件

for lf in "$logfiles"
引号保留了
find
输出中的空白。在这种情况下,引用是不正确的。删除它们将正确地迭代文件:

$ for i in "`find . -iname '*.log'`"; do echo $i; done
./2.log ./3.log ./1.log

$ for i in `find . -iname '*.log'`; do echo $i; done
./2.log
./3.log
./1.log
但是有一个更好的方法:您应该流式传输数据,而不是迭代。考虑这种模式:

$ find . -iname '*.log' | xargs -n 1 echo
./2.log
./3.log
./1.log
非常值得一提的是
xargs
,它将标准输入转换为附加参数,添加到自己的参数中,然后执行。在这个简单的例子中,我告诉xargs为每个1(
-n1
)文件分别运行命令
echo

有几个原因可以说明,
xargs
是我尽可能使用的迭代操作符:首先,它非常聪明。使用
for i in$(command)
迭代命令输出需要
$(command)
item1 item2 item3
的形式提供列表,如果任何项包含特殊字符,则会导致问题,然后bash会将这些字符解释为for参数的一部分

下面是一个在bash中作为有效输入字段的特殊空间的示例

$ for i in `find . -iname '*.log'`; do echo $i; done
./4
tricky.log
./2.log
./3.log
./1.log
包含空格的文件
4.log
现在导致了问题

xargs
可以聪明地将它们分开。在某些情况下,您可以通过更改输入字段分隔符
$IFS
来解决此问题。但这很快就会变得一团糟。有了
xargs
,您就有了更好的选择-具体地说,
xargs
还可以使用空字符以
-0
字符终止其输入流中的项目。其他程序,即
find
,也可以在其输出中使用空字符来匹配
xargs
所期望的内容。从这个意义上说,
xargs
find
是一个很好的组合:

$ find . -iname '*.log' -print0 | xargs -0 -n 1 echo
./4 tricky.log
./2.log
./3.log
./1.log
但是等等,还有更多!命令的下一步肯定是
grep
文件,查找您希望找到的匹配行。如果您的行很大,您也需要并行化
xargs
也可以这样做。您可以在管道中添加更多步骤以进行过滤等

最后,如果不小心使用子shell替换
$()
作为程序参数以避免在失败情况下出现意外参数,那么使用子shell替换作为程序参数可能会导致意外命令。我曾经写过一个脚本,它使用
$()
查找mysql的源目录来进行一些首次设置。它说的是类似于
remove-r/$(find/-inamemysqldir)
。好吧,如果在预期的位置没有变成
rm-r/
mysqldir
。显然不是我想要的:哦

这就是为什么我尽可能使用并鼓励其他人使用
xargs

当你说

for lf in "$logfiles"
引号保留了
find
输出中的空白。在这种情况下,引用是不正确的。删除它们将正确地迭代文件:

$ for i in "`find . -iname '*.log'`"; do echo $i; done
./2.log ./3.log ./1.log

$ for i in `find . -iname '*.log'`; do echo $i; done
./2.log
./3.log
./1.log
但是有一个更好的方法:您应该流式传输数据,而不是迭代。考虑这种模式:

$ find . -iname '*.log' | xargs -n 1 echo
./2.log
./3.log
./1.log
非常值得一提的是
xargs
,它将标准输入转换为附加参数,添加到自己的参数中,然后执行。在这个简单的例子中,我告诉xargs为每个1(
-n1
)文件分别运行命令
echo

有几个原因可以说明,
xargs
是我尽可能使用的迭代操作符:首先,它非常聪明。使用
for i in$(command)
迭代命令输出需要
$(command)
item1 item2 item3
的形式提供列表,如果任何项包含特殊字符,则会导致问题,然后bash会将这些字符解释为for参数的一部分

下面是一个在bash中作为有效输入字段的特殊空间的示例

$ for i in `find . -iname '*.log'`; do echo $i; done
./4
tricky.log
./2.log
./3.log
./1.log
包含空格的文件
4.log
现在导致了问题

xargs
可以聪明地将它们分开。在某些情况下,您可以通过更改输入字段分隔符
$IFS
来解决此问题。但这很快就会变得一团糟。有了
xargs
,您就有了更好的选择-具体地说,
xargs
还可以使用空字符以
-0
字符终止其输入流中的项目。其他程序,即
find
,也可以在其输出中使用空字符来匹配
xargs
所期望的内容。从这个意义上说,
xargs
find
是一个很好的组合:

$ find . -iname '*.log' -print0 | xargs -0 -n 1 echo
./4 tricky.log
./2.log
./3.log
./1.log
但是等等,还有更多!命令的下一步肯定是
grep
文件,查找您希望找到的匹配行。如果您的行很大,您也需要并行化
xargs
也可以这样做。您可以在管道中添加更多步骤以进行过滤等

最后,如果不小心使用子shell替换
$()
作为程序参数以避免在失败情况下出现意外参数,那么使用子shell替换作为程序参数可能会导致意外命令。我曾经写过一个脚本,它使用
$()
查找mysql的源目录来进行一些首次设置。它说的是类似于
remove-r/$(find/-inamemysqldir)
。好吧,如果在预期的位置没有变成
rm-r/
mysqldir