我的脚本的这一部分是比较文件的每一行来查找预设的string。 如果string不作为文件中的一行存在,则应将其附加到文件的末尾。
STRING=foobar cat "$FILE" | while read LINE do if [ "$STRING" == "$LINE" ]; then export ISLINEINFILE="yes" fi done if [ ! "$ISLINEINFILE" == yes ]; then echo "$LINE" >> "$FILE" fi
但是,看起来好像$ LINE和$ ISLINEINFILE在完成do循环时都被清除了。 我怎样才能避免这一点?
如果我们只想对代码进行最小限度的更改以使其正常工作,那么我们所需要做的就是切换输入重定向:
string=foobar while read line do if [ "$string" == "$line" ]; then islineinfile="yes" fi done <"$file" if [ ! "$islineinfile" == yes ]; then echo "$string" >> "$file" fi
在上面,我们改变了cat "$file" | while do ...done
cat "$file" | while do ...done
, while do...done<"$file"
。 有了这个改变, while
循环不再处于子shell中,因此循环中创建的shell变量将在循环完成后继续生效。
我相信你的整个脚本可以被替换为:
sed -i.bak '/^foobar$/H; ${x;s/././;x;t; s/$/\nfoobar/}' file*
上面的代码将foobar
添加到每个文件的末尾,这些文件还没有与^foobar$
匹配的行。
上面显示了file*
作为sed的最后一个参数。 这会将更改应用于与glob匹配的所有文件。 如果您愿意,可以单独列出特定的文件。
以上是在GNU sed(linux)上测试的。 BSD / OSX sed可能需要稍作修改。
awk -i inplace -vs="foobar" '$0==s{f=1} {print} ENDFILE{if (f==0) print s; f=0}' file*
像sed命令一样,这可以在一个命令中处理多个文件。
为什么在do循环中设置的变量消失?
它因为在shell管道组件中设置而消失。 大多数shell在子shell中运行管道的每个部分。 通过Unix设计,在子shell中设置的变量不会影响其父或任何已经运行的其他shell。
我怎样才能避免这一点?
有几种方法:
最简单的方法是使用不在子shell中运行管道的最后一个组件的shell。 这是ksh
默认行为,例如使用shebang:
#!/bin/ksh
当最后一个lastpipe
选项被设置时,这种行为也可以是bash
:
shopt -s lastpipe
您可以在设置它的相同子shell中使用该变量。 请注意,您的原始脚本缩进是错误的,可能会导致错误地假设if
块在管道内,而事实并非如此。 用括号括起整个块将纠正这个问题,并且将是最小的改变(两个额外的字符)使其工作:
STRING = foobar的 猫“$ FILE”| (当读LINE时 做 if [“$ STRING”==“$ LINE”]; 然后 导出ISLINEINFILE =“是” 科幻 DONE 如果[! “$ ISLINEINFILE”==是]; 然后 回声“$ LINE”>>“$ FILE” 科幻 )
尽管如此,该变量仍然会丢失。
cat
不必要的: STRING = foobar的 同时阅读LINE 做 if [“$ STRING”==“$ LINE”]; 然后 导出ISLINEINFILE =“是” 科幻 完成<“$ FILE” 如果[! “$ ISLINEINFILE”==是]; 然后 回声“$ LINE”>>“$ FILE” 科幻
sed
或gawk
。 有关标准合规性详细信息,另请参阅https://unix.stackexchange.com/a/144137/2594 。